diff --git a/.gemini/commands/cr/ios/silence-test.toml b/.gemini/commands/cr/ios/silence-test.toml
index a804b156..fb637e0 100644
--- a/.gemini/commands/cr/ios/silence-test.toml
+++ b/.gemini/commands/cr/ios/silence-test.toml
@@ -18,8 +18,8 @@
     *   Identify the required `<test_name>`, required `<silencing_type>`
         (`DISABLED` or `FLAKY`), and optional `<conditions>` from the user's
         input.
-    *   Example input: `/cr:test:silence-test testMyFeature FLAKY only on
-        iphone device`
+    *   Example input: `/cr:ios:silence-test testMyFeature FLAKY only on iphone
+        device`
     *   **If you can't parse all the required arguments:** Do not proceed with
         this task and print out the missing arguments to the user.
 
@@ -78,8 +78,11 @@
                     afc85ffeb6f263217e983d0c5cf2f4e2d4af50d5 shows an example of
                     that.
 
-                *   **Form Factor:** `if ([ChromeEarlGrey isCompactWidth]) {
-                    EARL_GREY_TEST_SKIPPED(@"Reason"); }`
+                *   **Screen width:** `if ([ChromeEarlGrey isCompactWidth]) {
+                    EARL_GREY_TEST_SKIPPED(@"Reason"); }`.
+
+                *   **Type of device (iphone or ipad):** `if ([ChromeEarlGrey
+                    isIPadIdiom]) { EARL_GREY_TEST_SKIPPED(@"Reason"); }`
 
                 *   **Multi-window:** `if (![ChromeEarlGrey
                     areMultipleWindowsSupported]) {
diff --git a/DEPS b/DEPS
index 557e0121..051e412 100644
--- a/DEPS
+++ b/DEPS
@@ -267,7 +267,7 @@
   # pathname relative to build/config/siso/backend_config, or absolute path.
   'reapi_backend_config_path': Str(''),
   # siso CIPD package version.
-  'siso_version': 'git_revision:f7020b54462c37f1b10a16e68563c338c9f14371',
+  'siso_version': 'git_revision:2eee1d6feaab76d99397d4d840bd369a428c01ea',
 
   # reclient options.
   # RBE project to download rewrapper config files for. Only needed if
@@ -305,31 +305,31 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': '54cbda79cd53866b327be47ff37f993dde2349d3',
+  'src_internal_revision': '828e9cd6a5fd8abbc249ac5b3b3df28e3cbcbe7c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '5479115ef5bfc73266e94e69459da8e1da872fbe',
+  'skia_revision': 'cc4a8b9e5c69f182ea856bb4e940068674eff0fe',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'b2f16db913db5f9ac7b2c7096c6f6c13326d1377',
+  'v8_revision': 'c01e7ca8a87d9a0d1d51e9a0dcf8c1eb44b6e3d4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '5c58505be7f434c053f4066081001373d21c4bd1',
+  'angle_revision': 'bd1efa99f9e19b882d8f09000a315ced599e86c3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '18d4f3db94079d05896692b9c6b3d905ec84bce7',
+  'swiftshader_revision': '794b0cfce1d828d187637e6d932bae484fbe0976',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '4fdc024595ef291b63d0f3219ee66ce286018ea1',
+  'pdfium_revision': '82dca105b0c782cf9aa1ae7098b62b8c47173562',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
-  'boringssl_revision': '88d0c0f4772f3abe74f4f1012fe580fa85bab417',
+  'boringssl_revision': '1ae74684a023cccf8d9e6d478e6ea25cd37f7eac',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
@@ -353,7 +353,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling breakpad
   # and whatever else without interference from each other.
-  'breakpad_revision': '8b96cc5d7a809bed83d7a0eeaaba6a5c3133a6f4',
+  'breakpad_revision': '85e647e1feb9599427a8fb1e3cd563de44d76557',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -373,11 +373,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '4ca50c8ae1cea0c8f2c87eae90c0fbcb6d7e262b',
+  'catapult_revision': 'f724a59d02db629831e1b4b3eff407cec902f49b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
-  'crossbench_revision': '414b4b87f2225616eecd5bca5025fc2e4ee6134c',
+  'crossbench_revision': 'fc2b2ed17dceee8def2ce9fd3135cc0b7efb215e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -389,7 +389,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling fuzztest
   # and whatever else without interference from each other.
-  'fuzztest_revision': '03093791de854345c32ce345f6b6a32b822ac940',
+  'fuzztest_revision': 'f13e4d833697557712f43f06ecb2e1e3b874801f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling domato
   # and whatever else without interference from each other.
@@ -397,7 +397,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '6bce0915c43dcb44304db70d05e52bcc35a096c6',
+  'devtools_frontend_revision': 'fa145518edf09e5c69c7ffb42b7aebcb6d1f3ece',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -421,7 +421,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '43d8fb0293947a3fec5c9ba187f16a9176597d31',
+  'dawn_revision': 'd952ec49e3d4b719c7196cfcb7d2a25b6c8b070d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -525,11 +525,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling llvm-libc
   # and whatever else without interference from each other.
-  'llvm_libc_revision':    'afad4d47e760ddc610e27653719192415b663798',
+  'llvm_libc_revision':    'b53ec82f9c6f262bae65028e981f9ba35e612642',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling llvm-libc
   # and whatever else without interference from each other.
-  'compiler_rt_revision': 'b213d0f0f24a2f1f682e4712b75f094f34c9c0e7',
+  'compiler_rt_revision': 'dda6158cb0a75b2ef6534e522c9dc20368ef617f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling clusterfuzz-data
   # and whatever else without interference from each other.
@@ -537,7 +537,7 @@
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
-  'libcxx_revision':       'cc5f928de999c9c86202d7eb1d0f8bdc3b53465f',
+  'libcxx_revision':       '4b6389141910f2bb5df4574b6e0e9ec5a1acb165',
 
   # GN CIPD package version.
   'gn_version': 'git_revision:81b24e01531ecf0eff12ec9359a555ec3944ec4e',
@@ -860,157 +860,157 @@
     'objects': [
       {
         # The Android libclang_rt.builtins libraries are currently only included in the Linux clang package.
-        'object_name': 'Linux_x64/clang-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '35a8629534f534aa6771470945fc0baa6906b3fffb28433bc08674d343b84c90',
-        'size_bytes': 55674480,
-        'generation': 1758743123214066,
+        'object_name': 'Linux_x64/clang-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': 'f6a487ffd0e56ba7a39b063d85d1f8ff7846514f50635785730cffb7368872ce',
+        'size_bytes': 55669844,
+        'generation': 1759771493989631,
         'condition': '(host_os == "linux" or checkout_android) and non_git_source',
       },
       {
-        'object_name': 'Linux_x64/clang-tidy-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '0342c1f9f546b2c87010418c37eaf494b3bcee24e60a351a880046951bf4d47b',
-        'size_bytes': 14059964,
-        'generation': 1758743123322050,
+        'object_name': 'Linux_x64/clang-tidy-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '4fc7aacf4c25e50a25a941f1186a9e042ae26a2c5c698f359907798fa68106c8',
+        'size_bytes': 14053336,
+        'generation': 1759771494041411,
         'condition': 'host_os == "linux" and checkout_clang_tidy and non_git_source',
       },
       {
-        'object_name': 'Linux_x64/clangd-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '8b9513babd89f706e928be51b9a4c08a4511dae1c152285808d7a25b299ae94b',
-        'size_bytes': 14210752,
-        'generation': 1758743123414815,
+        'object_name': 'Linux_x64/clangd-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '238897cb0b55ffcb7f6b8f6a10055e44e05023642441a800895704ced91d37d1',
+        'size_bytes': 14197108,
+        'generation': 1759771494144266,
         'condition': 'host_os == "linux" and checkout_clangd and non_git_source',
       },
       {
-        'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '56bb0093e2e8f71e682f03b0e379d7dac0bacfcc83bfccfd42a4fcd1310fbe75',
-        'size_bytes': 2272396,
-        'generation': 1758743123592944,
+        'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '2c5b0bf210ca982d8ec37cacf3d06d9c45bd6e68b33dcaabce0d108d6c266a36',
+        'size_bytes': 2272128,
+        'generation': 1759771494296549,
         'condition': 'host_os == "linux" and checkout_clang_coverage_tools and non_git_source',
       },
       {
-        'object_name': 'Linux_x64/llvmobjdump-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '9236697d64fc9444b22c90a112f6b3a76ee1edf5b3891af67de0849deb274514',
-        'size_bytes': 5666148,
-        'generation': 1758743123461779,
+        'object_name': 'Linux_x64/llvmobjdump-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': 'fd644634db56977b072d951f26571ac41c9c298bf5989e99efeb150ee8427364',
+        'size_bytes': 5666140,
+        'generation': 1759771494159187,
         'condition': '((checkout_linux or checkout_mac or checkout_android) and host_os == "linux") and non_git_source',
       },
       {
-        'object_name': 'Mac/clang-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '90e1a97b91d9a39bafc719f5e3b4c3cd8bf457c39f1dc4a27e4bfc59b9331bc5',
-        'size_bytes': 53576996,
-        'generation': 1758743125100350,
+        'object_name': 'Mac/clang-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '44811b6ed6868142c088807f6bcc0d08811a7b11d3f2bc2124c45868037e8cc3',
+        'size_bytes': 53583464,
+        'generation': 1759771495565305,
         'condition': 'host_os == "mac" and host_cpu == "x64"',
       },
       {
-        'object_name': 'Mac/clang-mac-runtime-library-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '7140b54db5936c79bb6f216ea176be70c7e6711f0dec2224369fba76cb9c1572',
-        'size_bytes': 1004900,
-        'generation': 1758743135101043,
+        'object_name': 'Mac/clang-mac-runtime-library-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '8a2e16410bede5d52c77a012f182dde2350b05e647f7c1acaf7823ce816b4422',
+        'size_bytes': 1005144,
+        'generation': 1759771503758969,
         'condition': 'checkout_mac and not host_os == "mac"',
       },
       {
-        'object_name': 'Mac/clang-tidy-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '0ef9124d5c56825ebbd10539298400a0b0d1d8d67e0902a7e89b3fecff7f9b0c',
-        'size_bytes': 14141008,
-        'generation': 1758743125225488,
+        'object_name': 'Mac/clang-tidy-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '66633fe8846fddeda86b5ee992b945939bfe46567c9c685900c39531d22ce5cf',
+        'size_bytes': 14133312,
+        'generation': 1759771495642847,
         'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_tidy',
       },
       {
-        'object_name': 'Mac/clangd-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': 'aec08495162681dbfe4e78bd6c728e6f1f410f3fe6c0e070c095dcf4bfda1382',
-        'size_bytes': 15632104,
-        'generation': 1758743125301839,
+        'object_name': 'Mac/clangd-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '44088b951aa7ddc96c0f32703b076311a7e7b803b3adfe0bfe9725f78c4fab29',
+        'size_bytes': 15627392,
+        'generation': 1759771495653658,
         'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clangd',
       },
       {
-        'object_name': 'Mac/llvm-code-coverage-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '36b279a1a6dc9d90e932823138f522e3c2741005e34732bce60fea60881a3963',
-        'size_bytes': 2321200,
-        'generation': 1758743125546947,
+        'object_name': 'Mac/llvm-code-coverage-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '79d62c78d256a508a0f3dbe59aa0fdf0391a9d462bf74e56adc1dee82efa83ac',
+        'size_bytes': 2321940,
+        'generation': 1759771495825689,
         'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_coverage_tools',
       },
       {
-        'object_name': 'Mac/llvmobjdump-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '29e8b2d333ecb6640cf99d9103b999ff2be0bb13fe8300528b4245bf6b88869c',
-        'size_bytes': 5582716,
-        'generation': 1758743125362967,
+        'object_name': 'Mac/llvmobjdump-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': 'a10d075e19e7b614ffd8c5a65f04fbd45011ec74c735dda89f0b3780ab397329',
+        'size_bytes': 5567160,
+        'generation': 1759771495741126,
         'condition': 'host_os == "mac" and host_cpu == "x64"',
       },
       {
-        'object_name': 'Mac_arm64/clang-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '29d82cb9830396c21b967a5784f838dcb3d62abfebd08d67d36821dba6eb4ce8',
-        'size_bytes': 44576940,
-        'generation': 1758743136591599,
+        'object_name': 'Mac_arm64/clang-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': 'c97e4f62cdd77edf725ccbf4cd63b589302605bf643c871f83214f39e629b2ea',
+        'size_bytes': 44593804,
+        'generation': 1759771504972271,
         'condition': 'host_os == "mac" and host_cpu == "arm64"',
       },
       {
-        'object_name': 'Mac_arm64/clang-tidy-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '8d7781b19bd032eeda7a94810e5429e0501392ac5585fcd16499a3d72e12ab9e',
-        'size_bytes': 12142468,
-        'generation': 1758743136678250,
+        'object_name': 'Mac_arm64/clang-tidy-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '3a0eb0fb3a4633c8b4b143e826c5476c41cdd6bd0db8e93a74bbee6520b02b79',
+        'size_bytes': 12136348,
+        'generation': 1759771505073378,
         'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_tidy',
       },
       {
-        'object_name': 'Mac_arm64/clangd-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '16617a896e7304ba76af9cbcab00edeb63753804237fc5055810b2049d00b3dc',
-        'size_bytes': 12474420,
-        'generation': 1758743136764487,
+        'object_name': 'Mac_arm64/clangd-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '2a5dc1f385bacd25b974b8aa15c57008e33bc384521e2d705a940acbb3292356',
+        'size_bytes': 12479180,
+        'generation': 1759771505148040,
         'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clangd',
       },
       {
-        'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '3ae73edf0d6b69d6aa41247c8268aaf292630f708036d55f3e0e5fa2ce340497',
-        'size_bytes': 1947856,
-        'generation': 1758743136945536,
+        'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '00bf0f82ca9aff15f32e7f0cf7e7b25d36a5a672a1a9bc345c1b7e140a478f93',
+        'size_bytes': 1948520,
+        'generation': 1759771505303586,
         'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_coverage_tools',
       },
       {
-        'object_name': 'Mac_arm64/llvmobjdump-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '069266d0ab2b9029908edc0b958af5d5ec3d9cd939b063da7aeeb53548137df9',
-        'size_bytes': 5277360,
-        'generation': 1758743136838343,
+        'object_name': 'Mac_arm64/llvmobjdump-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '7aa959752d6beafc74129e4822912021f855584e55a55600044f1d42b889f8b0',
+        'size_bytes': 5292960,
+        'generation': 1759771505201957,
         'condition': 'host_os == "mac" and host_cpu == "arm64"',
       },
       {
-        'object_name': 'Win/clang-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': 'bec899a7163ba0d446a5355e554cf8644b5e3db729404c6defb077549bc9f1b4',
-        'size_bytes': 47645664,
-        'generation': 1758743148772393,
+        'object_name': 'Win/clang-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': 'fc756186dea61e700bd0f885b585050d9356bbd7f942dafae25d38eef4671adf',
+        'size_bytes': 47657436,
+        'generation': 1759771514781908,
         'condition': 'host_os == "win"',
       },
       {
-        'object_name': 'Win/clang-tidy-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '10770b3b7b34a0e968cbeb1838b1446080897941c2bb5d192aa6596bbb386c27',
-        'size_bytes': 14025008,
-        'generation': 1758743148836717,
+        'object_name': 'Win/clang-tidy-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': 'f7ecd7e8d555e8622e0096ea1aca3ddb3fb4e89e91228c3c87289a4b8ca7919c',
+        'size_bytes': 14016476,
+        'generation': 1759771514824669,
         'condition': 'host_os == "win" and checkout_clang_tidy',
       },
       {
-        'object_name': 'Win/clang-win-runtime-library-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': 'fedf17614b4cba1c8edc7f3ad1c4636bb79535068e76ad6fed75fe65515dc4b8',
-        'size_bytes': 2503180,
-        'generation': 1758743159444585,
+        'object_name': 'Win/clang-win-runtime-library-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '0a426702c9e0f92ea27f9611a1665cc5df9a58820360d3fa6a4026b9a0e5120f',
+        'size_bytes': 2501292,
+        'generation': 1759771523074183,
         'condition': 'checkout_win and not host_os == "win"',
       },
       {
-        'object_name': 'Win/clangd-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '6dcd1c2f3bd7dbd547f8b93b014a3bc9f9d84b0920fc7632f45a6bfc1b359ae1',
-        'size_bytes': 14366920,
-        'generation': 1758743148925930,
+        'object_name': 'Win/clangd-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': 'b172d0246511cdeffbc5a4fa44ad402a6b9eacd9d3e2e77d88a9965f80d344d5',
+        'size_bytes': 14364312,
+        'generation': 1759771514873065,
        'condition': 'host_os == "win" and checkout_clangd',
       },
       {
-        'object_name': 'Win/llvm-code-coverage-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '57e86c6eeeccb1e6e5b87d87c2231f01e006d9067e2f3ad50530e32674599ad6',
-        'size_bytes': 2366460,
-        'generation': 1758743149180966,
+        'object_name': 'Win/llvm-code-coverage-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': 'b70528795afd95729971b74939e512c638a8a93fd1ee1c9205a6240f7af28802',
+        'size_bytes': 2368144,
+        'generation': 1759771515105244,
         'condition': 'host_os == "win" and checkout_clang_coverage_tools',
       },
       {
-        'object_name': 'Win/llvmobjdump-llvmorg-22-init-8940-g4d4cb757-1.tar.xz',
-        'sha256sum': '3f398db586e4f75a48eda2a508be4577a9c54cda78cf03afa57b454801ed5bde',
-        'size_bytes': 5668924,
-        'generation': 1758743148999346,
+        'object_name': 'Win/llvmobjdump-llvmorg-22-init-8940-g4d4cb757-84.tar.xz',
+        'sha256sum': '94c068f109e220e028a38f5beced7d6acd67725fc0b1da9fa8ed1b959f12d799',
+        'size_bytes': 5673824,
+        'generation': 1759771514962844,
         'condition': '(checkout_linux or checkout_mac or checkout_android) and host_os == "win"',
       },
     ]
@@ -1190,7 +1190,7 @@
       'packages': [
           {
               'package': 'chromium/chrome/android/orderfiles/arm',
-              'version': '7S8_NKGRG3_NXPC4OI_LiunV1RTQb31EQCkbc--rhyIC',
+              'version': '0zDR8Yo5-H6-vRW2XCtrOhOE6kPok5ZENQ44wDETmRQC',
           },
       ],
       'condition': 'checkout_android',
@@ -1201,7 +1201,7 @@
       'packages': [
           {
               'package': 'chromium/chrome/android/orderfiles/arm64',
-              'version': '8cQASWPpWi3p0inKkhIT72ccludnNFe0A-Z5oAKLZHUC',
+              'version': 'mMOHF_XwdmIch8_U5TmP_Se1sopcVL3r96aubohDdJwC',
           },
       ],
       'condition': 'checkout_android',
@@ -1212,7 +1212,7 @@
       'packages': [
           {
               'package': 'chromium/android_webview/tools/orderfiles/arm',
-              'version': 'P-oTL3gaTu66Rfl7KSEEFRcd7t36HWHTwx_Dqqf2th4C',
+              'version': 'B3fXpGR-q8Y8tRJ3-IJNBV_Eufej9Zq6OjsCs_smxg8C',
           },
       ],
       'condition': 'checkout_android',
@@ -1223,7 +1223,7 @@
       'packages': [
           {
               'package': 'chromium/android_webview/tools/orderfiles/arm64',
-              'version': 'y-9J5Lbs_559_UnuBO0as3tRnhTI8dLSXZefPUSPN7UC',
+              'version': 'f0Ser5e8eI12sDz4KTt6Sq53TSR6MxYXNcppsEbvWNUC',
           },
       ],
       'condition': 'checkout_android',
@@ -1395,7 +1395,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/updater/chromium_linux64',
-          'version': 'version:2@1520015',
+          'version': 'version:2@1521004',
         },
       ],
   },
@@ -1406,7 +1406,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/updater/chromium_mac_amd64',
-          'version': 'version:2@1520019',
+          'version': 'version:2@1521006',
         },
       ],
   },
@@ -1417,7 +1417,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/updater/chromium_mac_arm64',
-          'version': 'version:2@1520018',
+          'version': 'version:2@1521003',
         },
       ],
   },
@@ -1439,7 +1439,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/updater/chromium_win_x86',
-          'version': 'version:2@1520026',
+          'version': 'version:2@1521014',
         },
       ],
   },
@@ -1450,7 +1450,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/updater/chromium_win_x86_64',
-          'version': 'version:2@1520031',
+          'version': 'version:2@1521004',
         },
       ],
   },
@@ -1528,7 +1528,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/enterprise_companion/chromium_linux64',
-          'version': 'R8aDHj1lRHKzk_7QrfUTBIw4aWFRyBmBBI5BMnQeSxYC',
+          'version': 'VuY7G-r5ROPn1RhaX-CO0-h0tv3tf8OoFdXn4UWvQmAC',
         },
       ],
   },
@@ -1539,7 +1539,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/enterprise_companion/chromium_mac_amd64',
-          'version': 'zTERsFuqCNrkpcbj17eaJYkKhLu3Nd5W7dmPKyFEOk4C',
+          'version': 'X3YlXY7g01u10ZlwzhHD5DkS8y__F7FqPuMHNhEokwkC',
         },
       ],
   },
@@ -1550,7 +1550,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/enterprise_companion/chromium_mac_arm64',
-          'version': 'EQQqMPnjxZJZeVK2qWmJSuG8ozLZErCng1PdyiFvmIIC',
+          'version': '9TRuQAdh6f-LzeVpjGzo0V_-DI03ThMrX_ZwBxS69boC',
         },
       ],
   },
@@ -1572,7 +1572,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/enterprise_companion/chromium_win_x86_64',
-          'version': 'wheZQGI32MPrc31QBig1fOgkgeiqY0r6EFMPYnid3G0C',
+          'version': 'LLRKSK0DCJFxGt0AOSmULvF76jMXd6DRzGB9RYso-RUC',
         },
       ],
   },
@@ -1608,7 +1608,7 @@
     'packages': [
       {
         'package': 'chromium/chrome/test/data/variations/cipd',
-        'version': 'RzbtvtUatKjuGwC6GVuKJbVeFiK_18U9Grmxt6FQbtUC',
+        'version': 'VlK6Gtj_2NU56m8M3U95zrRPUmLgM796IZ89JvZ2QDUC',
       },
     ],
     'dep_type': 'cipd',
@@ -1619,7 +1619,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '42a6e482e94535fc8270814c8092516fc9996892',
+    '7cb2794b10723ee11ae31558250443ca39feeb79',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1719,7 +1719,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'zxErY7c91CwMav2QgwI7rJexUrBoYNamBqnZJj5S-FUC',
+          'version': 'nMqtwoij5t2aJ8glCR5zbos-Jbx8Ig7gRyAuXr-pdVkC',
       },
     ],
     'condition': 'checkout_android and non_git_source',
@@ -1790,7 +1790,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/error_prone',
-               'version': 'hWbH4ETtqHXUYdBJc_KBLXq1CEjQFmQylyHVQUszCSkC',
+               'version': '9sXUoP-IaXADzp7Q3pyXAmX-zszzSVd7t2QValkkss8C',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1812,7 +1812,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/lint',
-               'version': '3QcVzxF6pQW8pak81zGzUciRaonUh1NpS3UBjZZfVTsC',
+               'version': 'VyDrZ5fyFW5p9gU-4TnLHv97fdDn07NGu0uZMhZ5gfsC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1823,7 +1823,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/manifest_merger',
-               'version': '4cITVa3bS61rDFtbh_QoflvqlRrM-KrtViIyD4QEgjYC',
+               'version': '-QZQHB3DVUsl1l8AAX4iNcR7p3aFVQk6Bh4Bu9e8YfoC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -2055,7 +2055,7 @@
     Var('chromium_git') + '/chromium/web-tests.git' + '@' + Var('crossbench_web_tests_revision'),
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'eda3b48f2e32304d4ea20fee7ac1d37c9b060b3b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '88b3c4e0b1f356fce125752dbac35f9745767e0d',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -2303,7 +2303,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/kotlin_stdlib',
-              'version': 'vXobtILTB_x0fNrQEotY3gJBUpCRfE_s9HUU_u1arpEC',
+              'version': 'q1PdqgguOYzlgL0nL_2B3w0rIjtawmocRaDJqMjE0i4C',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -2579,7 +2579,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '652bdb7719f30b52b08e506645a7322ff1b2cc6f',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + '4654d7968ac1a1e825a84bd4d85acadd7993c49a',
+    Var('chromium_git') + '/openscreen' + '@' + 'efc3a38d81db2ec422035b221c0e90ec376ac8df',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '57572a0e91890fe7183de25a62153aec955d64ba',
@@ -2605,7 +2605,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'b6e6c4d600d07e3974db4db36e0010291ce49441',
+    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'd57eda6f192a4d5f147531d3f63355b5e36c5c78',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2782,7 +2782,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'SS09E3kPRdP5W064MT-pNrNhXBshbdgO58WU9Y8yY38C',
+              'version': '2aBDG942g42qUBPPInGETRHusdxru1U3anwJI_QX5wIC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -2823,10 +2823,10 @@
     Var('chromium_git') + '/external/github.com/google/ruy.git' + '@' + '9940fbf1e0c0863907e77e0600b99bb3e2bc2b9f',
 
   'src/third_party/search_engines_data/resources':
-    Var('chromium_git') + '/external/search_engines_data.git' + '@' + 'd5532f82099d33fb441b936ac9ecf832d275a3fa',
+    Var('chromium_git') + '/external/search_engines_data.git' + '@' + '4de7ff619acb3a46e43238a05a6ca981d7680e1c',
 
   'src/third_party/search_engines_data/resources_internal': {
-    'url': Var('chrome_git') + '/external/search_engines_data_internal.git' + '@' + 'b7e5ea3247cdce918cedf569c35a736bf6affda4',
+    'url': Var('chrome_git') + '/external/search_engines_data_internal.git' + '@' + '0547106e1f3f50a4b7e3f21ab4447ebf8ccf5eab',
     'condition': 'checkout_src_internal',
   },
 
@@ -2917,30 +2917,30 @@
       'packages': [
           {
               'package': 'chromium/third_party/turbine',
-              'version': 'LaDt9HreG5-Q6yBbZLf6LJSvLKqNjamn84VJgZHXSTEC',
+              'version': 'UPK1OhajOS06r3w9iwxehBFg7pbTwe9mgMcGkY6-VsEC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@207fff6486dbecfe236dd34ea206d5fc10a1f74f',
-  'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@a57276bf558f5cf94d3a9854ebdf5a2236849a5a',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@aa211644ec30b826d9bac4d80d90efe794f94733',
+  'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@f6652dcf751920b1fbc132619b0e84ef3d6e77c4',
   'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3',
   'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@01e0577914a75a2569c846778c2f93aa8e6feddd',
   'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@533a69c63ae773d90d03b206954ffffec6517dbd',
-  'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@a4f8ada9f4f97c45b8c89c57997be9cebaae65d2',
-  'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@ae0461b671558197a9a50e5fcfcc3b2d3f406b42',
-  'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@5568ce14705e512113df5b459fc86d857b3d7789',
-  'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@4d0b838ffcf1ef81151f0e7e11fad1d9ff859813',
-  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@9657ea8efe117f6b852dbf9974d6f77812880713',
+  'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@4eebce1723aced9afa272c38dfd4f82a786ed9ad',
+  'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@385716f0a63fe2c26e54a5140c9877a80da66592',
+  'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@8ebb2a62f5beee9447a8899a57db291d4034b0fc',
+  'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@a1e45945b3a84140956dc4672684090cf8e636a4',
+  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@270dbea039c2b485e33330be58a3e0ebcaef485f',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'cb0597213b0fcb999caa9ed08c2f88dc45eb7d50',
 
   # Display server protocol for Linux.
   'src/third_party/wayland/src': {
-      'url': Var('chromium_git') + '/external/anongit.freedesktop.org/git/wayland/wayland.git' + '@' + 'a156431ea66fe67d69c9fbba8a8ad34dabbab81c',
+      'url': Var('chromium_git') + '/external/anongit.freedesktop.org/git/wayland/wayland.git' + '@' + '736d12ac67c20c60dc406dc49bb06be878501f86',
       'condition': 'checkout_linux',
   },
 
@@ -2976,7 +2976,7 @@
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'b0db25b10ced338a04ce88106ed3e693cca96602',
+    Var('webrtc_git') + '/src.git' + '@' + '06fd850ea8e86ed78cf341d5f989b8ccbbd3266f',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -3098,7 +3098,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/boca_receiver_app/app',
-        'version': '9Da-gPQit0F2FRzvv1g7mZyEo1zJsLboydvl3a8cKtwC',
+        'version': 'vpLUs_skd70j2R-52fo5BS0nCFKWriRm2rirtx0y9I0C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3109,7 +3109,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/boca_app/app',
-        'version': '5QfdBpHYioNpVZW8Meaj6gpsy9U2LSRvoirU7wTvWncC',
+        'version': 'SkEGTI-xl4KJsoun1JVS4vt5KKc5-GRiEjYD_bhyJlgC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3120,7 +3120,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'AdGp6Xa0lCpIKMNsKJpZQLmZs5dHPAZfWnZM_T1zYhsC',
+        'version': 'a1-8RMWgwDB6-UU1dle0XpeenlVVrMuXhDfJEwqlvlUC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3131,7 +3131,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '8qBAcVXDXi6eIn69ja2rpCcLbRfIO9h-w96BX67OKHEC',
+        'version': 'tc8UtIJWtQJ8ylF4_bj-JdT5Md9zrMi2ddiR7EedpMIC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3164,7 +3164,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'Yt6QsJ9q9jwwNWPQz1o0IhwUOCsbcFzKzovNca9OCmsC',
+        'version': 'cBMASR6uhChqghfDBHCgawMj5I2-uKsNCW7XJI-JbLoC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3191,7 +3191,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_deps/autorolled',
-              'version': 'X9D-ObbadP8UmRikGrTiAE7AgxKmxBUgUQSmQ97aGrcC',
+              'version': 'wCC63bmCdKMJblLtzke3OhhdJLKpXKJFSBllXXTu4UAC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -3536,7 +3536,7 @@
 
   'src/chrome/browser/platform_experience/win': {
       'url': Var('chrome_git') + '/chrome/browser/platform_experience/win.git' + '@' +
-        '79f30db1b0f936ed6647c2cf386d0517582cb8a0',
+        '8687ef93f5137bac130198b3c0c39f5a4e83234b',
       'condition': 'checkout_src_internal',
   },
 
@@ -3685,7 +3685,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        '20c4cc39d64f47cd19ec46b550ec6cbbf0951ed7',
+        '257e69c2cd8457dd9d8d180c96295ad7ea650b1e',
       'condition': 'checkout_src_internal',
   },
 
@@ -3757,7 +3757,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        'd32e8bd0adb36c52d410b11234c3d94cf79a1859',
+        '221cebde5272f8a5404872c955eae9fe0251e1c2',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/agents/testing/constants.py b/agents/testing/constants.py
index 32c5164..7b2225f 100644
--- a/agents/testing/constants.py
+++ b/agents/testing/constants.py
@@ -6,3 +6,5 @@
 import pathlib
 
 CHROMIUM_SRC = pathlib.Path(__file__).resolve().parents[2]
+GEMINI_SANDBOX_IMAGE_URL = (
+    'us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox')
diff --git a/agents/testing/eval_prompts.py b/agents/testing/eval_prompts.py
index 655e953..ca7b078b 100755
--- a/agents/testing/eval_prompts.py
+++ b/agents/testing/eval_prompts.py
@@ -18,6 +18,9 @@
 import promptfoo_installation
 import workers
 
+sys.path.append(str(constants.CHROMIUM_SRC))
+from agents.extensions import install
+
 TESTCASE_EXTENSION = '.promptfoo.yaml'
 _SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX'
 _TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS'
@@ -191,7 +194,7 @@
         _build_chromium(src_path)
 
 
-def _fetch_sandbox_image(gemini_cli_bin: pathlib.Path | None = None) -> bool:
+def _fetch_sandbox_image() -> bool:
     """Pre-fetches the sandbox image.
 
     Args:
@@ -201,30 +204,31 @@
         True on success, False on failure.
     """
     logging.info('Pre-fetching sandbox image. This may take a minute...')
-    # Use a simple, non-destructive prompt to trigger the one-time
-    # sandbox image download.
-    with tempfile.TemporaryDirectory() as tmpdir:
-        try:
-            command = [gemini_cli_bin or 'gemini', '--sandbox', 'no-op']
-            subprocess.run(
-                command,
-                text=True,
-                check=True,
-                stdout=subprocess.PIPE,
-                stderr=subprocess.STDOUT,
-                cwd=tmpdir,
-            )
-            return True
-        except subprocess.CalledProcessError as e:
-            output = ''
-            if e.stdout:
-                output += f'\noutput:\n{e.stdout}'
-            logging.error(
-                'Failed to pre-fetch sandbox image: %s. This may be '
-                'because you are in an environment that does not support '
-                'sandboxing. Try running with --no-sandbox.%s', e, output)
+    image = ''
+    try:
+        version = install.get_gemini_version()
+        if not version:
+            logging.error('Failed to get gemini version.')
             return False
 
+        image = f'{constants.GEMINI_SANDBOX_IMAGE_URL}:{version}'
+        subprocess.run(
+            ['docker', 'pull', image],
+            check=True,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.STDOUT,
+        )
+        return True
+    except (subprocess.CalledProcessError, FileNotFoundError) as e:
+        output = ''
+        if hasattr(e, 'stdout') and e.stdout:
+            output += f'\noutput:\n{e.stdout}'
+        logging.error(
+            'Failed to pre-fetch sandbox image from %s: %s. This may be '
+            'because you are in an environment that does not support '
+            'sandboxing. Try running with --no-sandbox.%s', image, e, output)
+        return False
+
 
 def _run_prompt_eval_tests(args: argparse.Namespace) -> int:
     """Performs all the necessary steps to run prompt evaluation tests.
@@ -252,7 +256,7 @@
         promptfoo = promptfoo_installation.setup_promptfoo(
             promptfoo_dir, args.promptfoo_revision, args.promptfoo_version)
 
-    if args.sandbox and not _fetch_sandbox_image(args.gemini_cli_bin):
+    if args.sandbox and not _fetch_sandbox_image():
         return 1
 
     worker_options = workers.WorkerOptions(clean=not args.no_clean,
diff --git a/agents/testing/eval_prompts_unittest.py b/agents/testing/eval_prompts_unittest.py
index c11ff192..fd9d68fc 100755
--- a/agents/testing/eval_prompts_unittest.py
+++ b/agents/testing/eval_prompts_unittest.py
@@ -429,25 +429,47 @@
 class FetchSandboxImageUnittest(unittest.TestCase):
     """Unit tests for the `_fetch_sandbox_image` function."""
 
-    @mock.patch('subprocess.run')
-    def test_fetch_sandbox_image_success(self, mock_subprocess_run):
+    def setUp(self):
+        self.subprocess_run_patcher = mock.patch('subprocess.run')
+        self.mock_subprocess_run = self.subprocess_run_patcher.start()
+        self.addCleanup(self.subprocess_run_patcher.stop)
+
+        self.get_gemini_version_patcher = mock.patch(
+            'eval_prompts.install.get_gemini_version')
+        self.mock_get_gemini_version = self.get_gemini_version_patcher.start()
+        self.addCleanup(self.get_gemini_version_patcher.stop)
+
+        self.mock_get_gemini_version.return_value = '1.2.3'
+
+    def test_fetch_sandbox_image_success(self):
         """Tests that _fetch_sandbox_image returns true on success."""
-        mock_subprocess_run.return_value = subprocess.CompletedProcess(
-            args=['gemini', '--sandbox', 'no-op'],
-            returncode=0,
-            stdout='',
-        )
         with self.assertLogs(level='INFO') as cm:
             result = eval_prompts._fetch_sandbox_image()
             self.assertTrue(result)
             self.assertIn('Pre-fetching sandbox image', cm.output[0])
 
-    @mock.patch('subprocess.run')
-    def test_fetch_sandbox_image_failure(self, mock_subprocess_run):
+        self.mock_subprocess_run.assert_called_once_with(
+            [
+                'docker', 'pull',
+                'us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:1.2.3'
+            ],
+            check=True,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.STDOUT)
+
+    def test_fetch_sandbox_image_get_version_fails(self):
         """Tests that _fetch_sandbox_image returns false on failure."""
-        error = subprocess.CalledProcessError(returncode=1, cmd='gemini')
+        self.mock_get_gemini_version.return_value = None
+        with self.assertLogs(level='ERROR') as cm:
+            result = eval_prompts._fetch_sandbox_image()
+            self.assertFalse(result)
+            self.assertIn('Failed to get gemini version', cm.output[0])
+
+    def test_fetch_sandbox_image_docker_pull_fails(self):
+        """Tests that _fetch_sandbox_image returns false on failure."""
+        error = subprocess.CalledProcessError(returncode=1, cmd='docker')
         error.stdout = 'mocked output'
-        mock_subprocess_run.side_effect = error
+        self.mock_subprocess_run.side_effect = error
         with self.assertLogs(level='ERROR') as cm:
             result = eval_prompts._fetch_sandbox_image()
             self.assertFalse(result)
@@ -514,6 +536,11 @@
         self.mock_subprocess_run = subprocess_run_patcher.start()
         self.addCleanup(subprocess_run_patcher.stop)
 
+        fetch_sandbox_image_patcher = mock.patch(
+            'eval_prompts._fetch_sandbox_image')
+        self.mock_fetch_sandbox_image = fetch_sandbox_image_patcher.start()
+        self.addCleanup(fetch_sandbox_image_patcher.stop)
+
     def test_run_prompt_eval_tests_no_tests(self):
         """Tests that the function returns 1 if there are no tests to run."""
         self.mock_get_tests_to_run.return_value = []
@@ -598,15 +625,9 @@
         """Tests that _run_prompt_eval_tests exits and logs output if sandbox
         pre-fetch fails."""
         self.args.sandbox = True
-        error = subprocess.CalledProcessError(returncode=1, cmd='gemini')
-        error.stdout = 'mocked output'
-        self.mock_subprocess_run.side_effect = error
-
-        with self.assertLogs(level='ERROR') as cm:
-            result = eval_prompts._run_prompt_eval_tests(self.args)
-            self.assertEqual(result, 1)
-            self.assertIn('Failed to pre-fetch sandbox image', cm.output[0])
-            self.assertIn('mocked output', cm.output[0])
+        self.mock_fetch_sandbox_image.return_value = False
+        result = eval_prompts._run_prompt_eval_tests(self.args)
+        self.assertEqual(result, 1)
 
     def test_run_prompt_eval_tests_with_sandbox_enabled(self):
         """Tests that _run_prompt_eval_tests calls pre-fetch and passes sandbox
@@ -614,17 +635,11 @@
         self.args.sandbox = True
         self.mock_worker_pool.return_value.wait_for_all_queued_tests.\
             return_value = []
+        self.mock_fetch_sandbox_image.return_value = True
 
         eval_prompts._run_prompt_eval_tests(self.args)
 
-        self.mock_subprocess_run.assert_called_once_with(
-            ['gemini', '--sandbox', 'no-op'],
-            text=True,
-            check=True,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.STDOUT,
-            cwd=mock.ANY,
-        )
+        self.mock_fetch_sandbox_image.assert_called_once()
         self.mock_worker_pool.assert_called_once()
         self.assertTrue(self.mock_worker_pool.call_args[0][2].sandbox)
 
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index e017a06..f543e4d 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -203,10 +203,7 @@
       is_default_(is_default),
       context_storage_path_(BuildStoragePath(relative_path_)),
       http_cache_path_(BuildHttpCachePath(relative_path_)),
-      simple_factory_key_(GetPath(), IsOffTheRecord()),
-      cookie_encryption_provider_(
-          std::make_unique<CookieEncryptionProviderImpl>(
-              AwBrowserProcess::GetInstance()->GetOSCryptAsync())) {
+      simple_factory_key_(GetPath(), IsOffTheRecord()) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   TRACE_EVENT("startup", "AwBrowserContext::AwBrowserContext", "name", name_);
 
@@ -587,8 +584,6 @@
           switches::kWebViewEnableModernCookieSameSite)
           ? network::mojom::CookieAccessDelegateType::ALWAYS_NONLEGACY
           : network::mojom::CookieAccessDelegateType::ALWAYS_LEGACY;
-  context_params->cookie_encryption_provider =
-      cookie_encryption_provider_->BindNewRemote();
 
   context_params->initial_ssl_config = network::mojom::SSLConfig::New();
   // Allow SHA-1 to be used for locally-installed trust anchors, as WebView
diff --git a/android_webview/browser/aw_browser_context.h b/android_webview/browser/aw_browser_context.h
index eccfcf91..41f199a 100644
--- a/android_webview/browser/aw_browser_context.h
+++ b/android_webview/browser/aw_browser_context.h
@@ -37,7 +37,6 @@
 #include "content/public/browser/zoom_level_delegate.h"
 #include "net/http/http_request_headers.h"
 #include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom-forward.h"
-#include "services/network/public/cpp/cookie_encryption_provider_impl.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom-shared.h"
 
@@ -302,8 +301,6 @@
   // In generally, use GetCookieManager() rather than using this directly.
   std::unique_ptr<CookieManager> cookie_manager_;
 
-  std::unique_ptr<CookieEncryptionProviderImpl> cookie_encryption_provider_;
-
   std::unique_ptr<AwPrefetchManager> prefetch_manager_;
   std::unique_ptr<AwPreconnector> preconnector_;
 
diff --git a/android_webview/common/aw_feature_map.cc b/android_webview/common/aw_feature_map.cc
index 31e7292..5c632c3e 100644
--- a/android_webview/common/aw_feature_map.cc
+++ b/android_webview/common/aw_feature_map.cc
@@ -50,6 +50,7 @@
     &features::kWebViewInvokeZoomPickerOnGSU,
     &features::kWebViewLazyFetchHandWritingIcon,
     &features::kWebViewMixedContentAutoupgrades,
+    &features::kWebViewMoveWorkToProviderInit,
     &features::kWebViewMuteAudio,
     &features::kWebViewOptInToGmsBindServiceOptimization,
     &features::kWebViewPrefetchNativeLibrary,
diff --git a/android_webview/common/aw_features.cc b/android_webview/common/aw_features.cc
index 75587c7..fc9e382 100644
--- a/android_webview/common/aw_features.cc
+++ b/android_webview/common/aw_features.cc
@@ -290,4 +290,9 @@
 BASE_FEATURE(kWebViewOptInToGmsBindServiceOptimization,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Moves some of the work that is being run during
+// `startChromium` to be done beforehand during WebView provider
+// initialization. This is expected to improve startup performance especially
+// when async startup takes place.
+BASE_FEATURE(kWebViewMoveWorkToProviderInit, base::FEATURE_DISABLED_BY_DEFAULT);
 }  // namespace android_webview::features
diff --git a/android_webview/common/aw_features.h b/android_webview/common/aw_features.h
index d63cec3..48e3838 100644
--- a/android_webview/common/aw_features.h
+++ b/android_webview/common/aw_features.h
@@ -72,7 +72,7 @@
 BASE_DECLARE_FEATURE(kWebViewEarlyPerfettoInit);
 BASE_DECLARE_FEATURE(kWebViewCacheBoundaryInterfaceMethods);
 BASE_DECLARE_FEATURE(kWebViewOptInToGmsBindServiceOptimization);
-
+BASE_DECLARE_FEATURE(kWebViewMoveWorkToProviderInit);
 }  // namespace android_webview::features
 
 #endif  // ANDROID_WEBVIEW_COMMON_AW_FEATURES_H_
diff --git a/android_webview/glue/BUILD.gn b/android_webview/glue/BUILD.gn
index a05dd1c..96aed48b 100644
--- a/android_webview/glue/BUILD.gn
+++ b/android_webview/glue/BUILD.gn
@@ -13,6 +13,7 @@
     "//android_webview:android_webview_product_config_java",
     "//android_webview:browser_java",
     "//android_webview:common_java",
+    "//android_webview:common_platform_services_java",
     "//android_webview:resources",
     "//android_webview/support_library/boundary_interfaces:boundary_interface_java",
     "//android_webview/support_library/callback:callback_java",
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
index 2519c17..89250164 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
@@ -38,8 +38,10 @@
 import org.chromium.android_webview.HttpAuthDatabase;
 import org.chromium.android_webview.R;
 import org.chromium.android_webview.WebViewChromiumRunQueue;
+import org.chromium.android_webview.common.AwFeatures;
 import org.chromium.android_webview.common.AwResource;
 import org.chromium.android_webview.common.Lifetime;
+import org.chromium.android_webview.common.WebViewCachedFlags;
 import org.chromium.android_webview.gfx.AwDrawFnImpl;
 import org.chromium.android_webview.metrics.TrackExitReasons;
 import org.chromium.android_webview.variations.FastVariationsSeedSafeModeAction;
@@ -528,6 +530,26 @@
         mIsStartupTasksYieldToNativeExperimentEnabled = enabled;
     }
 
+    // These are startup tasks that can either run during provider init or during `startChromium`.
+    // This is extracted out so that we can experiment with calling this in either of these
+    // locations.
+    public void runNonUiThreadCapableStartupTasks() {
+        ResourceBundle.setAvailablePakLocales(AwLocaleConfig.getWebViewSupportedPakLocales());
+
+        try (DualTraceEvent ignored2 = DualTraceEvent.scoped("LibraryLoader.ensureInitialized")) {
+            LibraryLoader.getInstance().ensureInitialized();
+        }
+
+        // TODO(crbug.com/400414092): PathService overrides should be obsolete now.
+        PathService.override(PathService.DIR_MODULE, "/system/lib/");
+        PathService.override(DIR_RESOURCE_PAKS_ANDROID, "/system/framework/webview/paks");
+
+        initPlatSupportLibrary();
+        AwContentsStatics.setCheckClearTextPermitted(
+                ContextUtils.getApplicationContext().getApplicationInfo().targetSdkVersion
+                        >= Build.VERSION_CODES.O);
+    }
+
     // Initializes a new StartupTaskRunner with a list of tasks to run for chromium startup.
     // Postcondition of calling `.run` on the returned StartupTasksRunner is that Chromium startup
     // is finished.
@@ -550,25 +572,11 @@
                         TrackExitReasons.startTrackingStartup();
                     }
 
-                    final Context context = ContextUtils.getApplicationContext();
-
-                    ResourceBundle.setAvailablePakLocales(
-                            AwLocaleConfig.getWebViewSupportedPakLocales());
-
-                    try (DualTraceEvent ignored2 =
-                            DualTraceEvent.scoped("WebViewChromiumAwInit.LibraryLoader")) {
-                        LibraryLoader.getInstance().ensureInitialized();
+                    if (!WebViewCachedFlags.get()
+                            .isCachedFeatureEnabled(
+                                    AwFeatures.WEBVIEW_MOVE_WORK_TO_PROVIDER_INIT)) {
+                        runNonUiThreadCapableStartupTasks();
                     }
-
-                    // TODO(crbug.com/400414092): These should be obsolete now.
-                    PathService.override(PathService.DIR_MODULE, "/system/lib/");
-                    PathService.override(
-                            DIR_RESOURCE_PAKS_ANDROID, "/system/framework/webview/paks");
-
-                    initPlatSupportLibrary();
-                    AwContentsStatics.setCheckClearTextPermitted(
-                            context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O);
-
                     waitUntilSetUpResources();
                     // NOTE: Finished writing Java resources. From this point on, it's safe
                     // to use them.
@@ -612,7 +620,7 @@
                     } catch (Resources.NotFoundException e) {
                         RecordHistogram.recordBooleanHistogram(
                                 ASSET_PATH_WORKAROUND_HISTOGRAM_NAME, true);
-                        mFactory.addWebViewAssetPath(context);
+                        mFactory.addWebViewAssetPath(ContextUtils.getApplicationContext());
                     }
 
                     AwBrowserProcess.configureChildProcessLauncher();
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
index d253177..c963ce4 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -57,6 +57,7 @@
 import org.chromium.android_webview.common.DeveloperModeUtils;
 import org.chromium.android_webview.common.FlagOverrideHelper;
 import org.chromium.android_webview.common.Lifetime;
+import org.chromium.android_webview.common.PlatformServiceBridge;
 import org.chromium.android_webview.common.ProductionSupportedFlagList;
 import org.chromium.android_webview.common.SafeModeController;
 import org.chromium.android_webview.common.WebViewCachedFlags;
@@ -74,6 +75,8 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.ScopedSysTraceEvent;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
 import org.chromium.base.version_info.VersionConstants;
 import org.chromium.blink_public.common.BlinkFeatures;
 import org.chromium.build.BuildConfig;
@@ -677,6 +680,16 @@
                 mAwInit.startVariationsInit();
             }
 
+            if (WebViewCachedFlags.get()
+                    .isCachedFeatureEnabled(AwFeatures.WEBVIEW_MOVE_WORK_TO_PROVIDER_INIT)) {
+                PostTask.postTask(
+                        TaskTraits.USER_VISIBLE,
+                        () -> {
+                            PlatformServiceBridge.getInstance();
+                        });
+                mAwInit.runNonUiThreadCapableStartupTasks();
+            }
+
             FlagOverrideHelper helper =
                     new FlagOverrideHelper(ProductionSupportedFlagList.sFlagList);
             helper.applyFlagOverrides(
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index c3129d2..8f4d7f27 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -253,9 +253,6 @@
                 AutofillFeatures.AUTOFILL_BETTER_LOCAL_HEURISTIC_PLACEHOLDER_SUPPORT,
                 "Treats placeholders as a separate signal for Autofill local heuristics"),
         Flag.baseFeature(
-                AutofillFeatures.AUTOFILL_DETECT_FIELD_VISIBILITY,
-                "Populates FormFieldData::is_visible() with visibility rather than focusability"),
-        Flag.baseFeature(
                 AutofillFeatures.AUTOFILL_ENABLE_EXPIRATION_DATE_IMPROVEMENTS,
                 "Enables various improvements to handling expiration dates."),
         Flag.baseFeature(
@@ -299,10 +296,6 @@
                 "When enabled, AutofillAgent will store its cached form and fields as renderer ids "
                         + "instead of holding strong references to blink::WebElement objects."),
         Flag.baseFeature(
-                AutofillFeatures.AUTOFILL_UNIFY_RATIONALIZATION_AND_SECTIONING_ORDER,
-                "When enabled, the same rationalization/sectioning order is used for heuristic and"
-                        + " server predictions."),
-        Flag.baseFeature(
                 AutofillFeatures.AUTOFILL_USE_IN_ADDRESS_MODEL,
                 "When enabled, Autofill uses a custom address model for India."),
         Flag.baseFeature(
@@ -1148,6 +1141,14 @@
         Flag.baseFeature(
                 AwFeatures.WEBVIEW_OPT_IN_TO_GMS_BIND_SERVICE_OPTIMIZATION,
                 "Opt-in WebView to GMSCore's bindService optimizations"),
+        Flag.baseFeature(
+                AwFeatures.WEBVIEW_MOVE_WORK_TO_PROVIDER_INIT,
+                " Moves some of the work that is being run during `startChromium` to be done"
+                    + " beforehand during WebView provider initialization. This is expected to"
+                    + " improve startup performance especially when async startup takes place."),
+        Flag.baseFeature(
+                "OriginMatcherNewCopyAssignment",
+                "Use a faster implementation for copying OriginMatchers."),
         // Add new commandline switches and features above. The final entry should have a
         // trailing comma for cleaner diffs.
     };
diff --git a/android_webview/java/src/org/chromium/android_webview/common/WebViewCachedFlags.java b/android_webview/java/src/org/chromium/android_webview/common/WebViewCachedFlags.java
index 04ff0cec..3b44f014 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/WebViewCachedFlags.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/WebViewCachedFlags.java
@@ -78,28 +78,42 @@
             sInstance =
                     new WebViewCachedFlags(
                             prefs,
-                            Map.of(
-                                    // Add new CachedFlags here along with their default state.
-                                    AwFeatures.WEBVIEW_DISABLE_CHIPS,
-                                    DefaultState.DISABLED,
-                                    AwFeatures.WEBVIEW_EARLY_PERFETTO_INIT,
-                                    DefaultState.DISABLED,
-                                    AwFeatures.WEBVIEW_EARLY_STARTUP_TRACING,
-                                    DefaultState.DISABLED,
-                                    AwFeatures.WEBVIEW_USE_STARTUP_TASKS_LOGIC,
-                                    DefaultState.DISABLED,
-                                    AwFeatures.WEBVIEW_USE_STARTUP_TASKS_LOGIC_P2,
-                                    DefaultState.DISABLED,
-                                    AwFeatures.WEBVIEW_STARTUP_TASKS_YIELD_TO_NATIVE,
-                                    DefaultState.DISABLED,
-                                    AwFeatures.WEBVIEW_REDUCED_SEED_EXPIRATION,
-                                    DefaultState.DISABLED,
-                                    AwFeatures.WEBVIEW_REDUCED_SEED_REQUEST_PERIOD,
-                                    DefaultState.DISABLED,
-                                    TracingServiceFeatures.ENABLE_PERFETTO_SYSTEM_TRACING,
-                                    DefaultState.DISABLED,
-                                    AwFeatures.WEBVIEW_OPT_IN_TO_GMS_BIND_SERVICE_OPTIMIZATION,
-                                    DefaultState.DISABLED));
+                            // Add new CachedFlags here along with their default state.
+                            Map.ofEntries(
+                                    Map.entry(
+                                            AwFeatures.WEBVIEW_DISABLE_CHIPS,
+                                            DefaultState.DISABLED),
+                                    Map.entry(
+                                            AwFeatures.WEBVIEW_MOVE_WORK_TO_PROVIDER_INIT,
+                                            DefaultState.DISABLED),
+                                    Map.entry(
+                                            AwFeatures.WEBVIEW_EARLY_PERFETTO_INIT,
+                                            DefaultState.DISABLED),
+                                    Map.entry(
+                                            AwFeatures.WEBVIEW_EARLY_STARTUP_TRACING,
+                                            DefaultState.DISABLED),
+                                    Map.entry(
+                                            AwFeatures.WEBVIEW_USE_STARTUP_TASKS_LOGIC,
+                                            DefaultState.DISABLED),
+                                    Map.entry(
+                                            AwFeatures.WEBVIEW_USE_STARTUP_TASKS_LOGIC_P2,
+                                            DefaultState.DISABLED),
+                                    Map.entry(
+                                            AwFeatures.WEBVIEW_STARTUP_TASKS_YIELD_TO_NATIVE,
+                                            DefaultState.DISABLED),
+                                    Map.entry(
+                                            AwFeatures.WEBVIEW_REDUCED_SEED_EXPIRATION,
+                                            DefaultState.DISABLED),
+                                    Map.entry(
+                                            AwFeatures.WEBVIEW_REDUCED_SEED_REQUEST_PERIOD,
+                                            DefaultState.DISABLED),
+                                    Map.entry(
+                                            TracingServiceFeatures.ENABLE_PERFETTO_SYSTEM_TRACING,
+                                            DefaultState.DISABLED),
+                                    Map.entry(
+                                            AwFeatures
+                                                    .WEBVIEW_OPT_IN_TO_GMS_BIND_SERVICE_OPTIMIZATION,
+                                            DefaultState.DISABLED)));
         }
     }
 
diff --git a/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt b/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
index badaccb0..e2805fd4 100644
--- a/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
@@ -6865,6 +6865,7 @@
     method constructor
     method populateMatrix
 interface Origin
+    static method from
     static method fromURL
     static method parse
     attribute @@toStringTag
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index fe0282a..bcf6c95b 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -2954,6 +2954,12 @@
       </message>
 
       <!-- Status tray charging strings. -->
+      <message name="IDS_ASH_STATUS_TRAY_INCOMPATIBLE_CHARGER_TITLE" desc="The title of a notification indicating that an incompatible charger has been connected, and no charging will occur." translateable="false">
+        Incompatible charger
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_INCOMPATIBLE_CHARGER_MESSAGE" desc="The message body of a notification indicating that an incompatible charger has been connected, and that the user needs to connect a more powerful one." translateable="false">
+        Connect an adapter with more watts (W) to charge your device.
+      </message>
       <message name="IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE" desc="The title of a notification indicating that a low-current USB charger has been connected.">
         Low-power charger connected
       </message>
@@ -3111,6 +3117,9 @@
       <message name="IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE_ACCESSIBLE" desc="The message used by accessibility to indicate that battery charging is unreliable.">
         Plugged in to a low-power charger. Battery charging may not be reliable.
       </message>
+      <message name="IDS_ASH_STATUS_TRAY_BATTERY_INCOMPATIBLE_CHARGER_ACCESSIBLE" desc="The message used by accessibility to announce that an incompatible charger is connected." translateable="false">
+        Plugged in to an incompatible charger. Connect the original charger (or a similar wattage charger) to charge your device.
+      </message>
       <message name="IDS_ASH_STATUS_TRAY_BATTERY_STATUS_SEPARATOR" desc="The separator symbol between battery percentage string and battery remaining time string">
         ''' - '''
       </message>
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc
index ec5526b..8c942307 100644
--- a/ash/capture_mode/capture_mode_controller.cc
+++ b/ash/capture_mode/capture_mode_controller.cc
@@ -1537,15 +1537,15 @@
 
 void CaptureModeController::SetSystemMediaDeviceStatus(
     crosapi::mojom::VideoConferenceMediaDevice device,
-    bool disabled,
+    bool enabled,
     SetSystemMediaDeviceStatusCallback callback) {
   switch (device) {
     case crosapi::mojom::VideoConferenceMediaDevice::kCamera:
-      is_camera_muted_ = disabled;
+      is_camera_muted_ = !enabled;
       std::move(callback).Run(true);
       return;
     case crosapi::mojom::VideoConferenceMediaDevice::kMicrophone:
-      is_microphone_muted_ = disabled;
+      is_microphone_muted_ = !enabled;
       std::move(callback).Run(true);
       return;
     case crosapi::mojom::VideoConferenceMediaDevice::kUnusedDefault:
diff --git a/ash/capture_mode/capture_mode_controller.h b/ash/capture_mode/capture_mode_controller.h
index 578925e..d61d2ed 100644
--- a/ash/capture_mode/capture_mode_controller.h
+++ b/ash/capture_mode/capture_mode_controller.h
@@ -444,7 +444,7 @@
                    ReturnToAppCallback callback) override;
   void SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice device,
-      bool disabled,
+      bool enabled,
       SetSystemMediaDeviceStatusCallback callback) override;
   void StopAllScreenShare() override;
 
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index d0a048b..93a93d4 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -279,6 +279,9 @@
 // Enables or disables sharing student's screen in the Boca app.
 BASE_FEATURE(kBocaScreenSharingStudent, base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Enables or disables sharing host audio in the Boca app.
+BASE_FEATURE(kBocaHostAudio, base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kCrosSwitcher, base::FEATURE_DISABLED_BY_DEFAULT);
 
 // Indicates whether the camera super resolution is supported. Note that this
@@ -1379,10 +1382,13 @@
 // Feature to allow MAC address randomization to be enabled for WiFi networks.
 BASE_FEATURE(kMacAddressRandomization, base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Enabling this flag allows the managed local pin and password related changes to be
-// applied.
+// Enabling this flag allows the managed local pin and password related changes
+// to be applied.
 BASE_FEATURE(kManagedLocalPinAndPassword, base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Enables policy management for USB printers.
+BASE_FEATURE(kManagedUsbPrinters, base::FEATURE_ENABLED_BY_DEFAULT);
+
 // Enables Mahi on PDF contents in the Media App.
 BASE_FEATURE(kMediaAppPdfMahi, base::FEATURE_ENABLED_BY_DEFAULT);
 
@@ -2024,6 +2030,10 @@
 BASE_FEATURE(kDemoModeSecondaryGoogleAccountSigninAllowedFalse,
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Controls whether to include the device info in the demo account setup request
+// to the demo server in signed-in experience.
+BASE_FEATURE(kSendDeviceInfoToDemoServer, base::FEATURE_ENABLED_BY_DEFAULT);
+
 // Moves toasts to the bottom-side corner where the status area is instead of
 // the center when enabled.
 BASE_FEATURE(kSideAlignedToasts, base::FEATURE_DISABLED_BY_DEFAULT);
@@ -2563,6 +2573,10 @@
   return base::FeatureList::IsEnabled(kBocaScreenSharingStudent);
 }
 
+bool IsBocaHostAudioEnabled() {
+  return base::FeatureList::IsEnabled(kBocaHostAudio);
+}
+
 bool IsBrightnessControlInSettingsEnabled() {
   return base::FeatureList::IsEnabled(kEnableBrightnessControlInSettings);
 }
@@ -2697,6 +2711,10 @@
       kDemoModeSecondaryGoogleAccountSigninAllowedFalse);
 }
 
+bool IsSendDeviceInfoToDemoServerEnabled() {
+  return base::FeatureList::IsEnabled(kSendDeviceInfoToDemoServer);
+}
+
 bool IsEcheSWAEnabled() {
   return base::FeatureList::IsEnabled(kEcheSWA);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 8def5fa..0b36d34 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -129,6 +129,7 @@
 BASE_DECLARE_FEATURE(kBocaScreenSharingTeacher);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kBocaScreenSharingStudent);
+COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kBocaHostAudio);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCameraSuperResSupported);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCrosSwitcher);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kBorealisBigGl);
@@ -632,6 +633,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kManagedLocalPinAndPassword);
 COMPONENT_EXPORT(ASH_CONSTANTS)
+BASE_DECLARE_FEATURE(kManagedUsbPrinters);
+COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kFeatureManagementLobster);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kLockScreenHideSensitiveNotificationsSupport);
@@ -891,6 +894,8 @@
 BASE_DECLARE_FEATURE(kDemoSessionToSNotification);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kDemoModeSecondaryGoogleAccountSigninAllowedFalse);
+COMPONENT_EXPORT(ASH_CONSTANTS)
+BASE_DECLARE_FEATURE(kSendDeviceInfoToDemoServer);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kSideAlignedToasts);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kSmartDimExperimentalComponent);
@@ -1082,6 +1087,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBocaCourseWorkMaterialApiEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBocaScreenSharingTeacherEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBocaScreenSharingStudentEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBocaHostAudioEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBrightnessControlInSettingsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCaptureModeEducationEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
@@ -1102,6 +1108,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDeepLinkingEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoModeAppLandscapeLockedEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoSessionToSNotificationEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsSendDeviceInfoToDemoServerEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoModeAppResetWindowContainerEnable();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoModeSignInEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoModeWallpaperUpdateEnabled();
diff --git a/ash/constants/notifier_catalogs.h b/ash/constants/notifier_catalogs.h
index 324c4443..6f39791d 100644
--- a/ash/constants/notifier_catalogs.h
+++ b/ash/constants/notifier_catalogs.h
@@ -217,7 +217,8 @@
   kUsbPeripheralDeviceOrEndpointLimit = 198,
   kDemoMode = 199,
   kArcDlcInstall = 200,
-  kMaxValue = kArcDlcInstall
+  kIncompatibleCharger = 201,
+  kMaxValue = kIncompatibleCharger
 };
 
 // A living catalog that registers system nudges.
diff --git a/ash/constants/web_app_id_constants.h b/ash/constants/web_app_id_constants.h
index 22749e99..22a7a522 100644
--- a/ash/constants/web_app_id_constants.h
+++ b/ash/constants/web_app_id_constants.h
@@ -203,6 +203,10 @@
 inline constexpr char kShowtimeAppId[] = "eoccpgmpiempcflglfokeengliildkag";
 
 // Generated as: web_app::GenerateAppId(/*manifest_id=*/std::nullopt, GURL(
+//     "https://docs.google.com/videos/?usp=installed_webapp"))
+inline constexpr char kVidsAppId[] = "cfkjhppcnjbkjoclfcjjkphpicbpoaeg";
+
+// Generated as: web_app::GenerateAppId(/*manifest_id=*/std::nullopt, GURL(
 //     "https://www.youtube.com/?feature=ytca"))
 inline constexpr char kYoutubeAppId[] = "agimnkijcaahngcdmfeangaknmldooml";
 inline constexpr char kYoutubeManifestId[] =
diff --git a/ash/public/cpp/message_center/arc_notifications_host_initializer.h b/ash/public/cpp/message_center/arc_notifications_host_initializer.h
index 8612c8df..8c1248b 100644
--- a/ash/public/cpp/message_center/arc_notifications_host_initializer.h
+++ b/ash/public/cpp/message_center/arc_notifications_host_initializer.h
@@ -17,23 +17,16 @@
  public:
   class Observer : public base::CheckedObserver {
    public:
-    // Invoked when ARC notifications instance is ready.
-    virtual void OnSetArcNotificationsInstance(
+    // Invoked when ArcNotificationManager is initialized.
+    virtual void OnArcNotificationManagerInitialized(
         ArcNotificationManagerBase* arc_notification_manager) = 0;
-
-    // Invoked when the ArcNotificationsHostInitializer object (the thing that
-    // this observer observes) will be destroyed. In response, the observer,
-    // |this|, should call "RemoveObserver(this)", whether directly or
-    // indirectly (e.g. via ScopedObservation::Reset).
-    virtual void OnArcNotificationInitializerDestroyed(
-        ArcNotificationsHostInitializer* initializer) = 0;
   };
 
   static ArcNotificationsHostInitializer* Get();
 
-  virtual void SetArcNotificationManagerInstance(
-      std::unique_ptr<ArcNotificationManagerBase> manager_instance) = 0;
-  virtual ArcNotificationManagerBase* GetArcNotificationManagerInstance() = 0;
+  virtual void SetArcNotificationManager(
+      std::unique_ptr<ArcNotificationManagerBase> arc_notification_manager) = 0;
+  virtual ArcNotificationManagerBase* GetArcNotificationManager() = 0;
 
   // Adds and removes an observer.
   virtual void AddObserver(Observer* observer) = 0;
diff --git a/ash/system/notification_center/message_center_controller.cc b/ash/system/notification_center/message_center_controller.cc
index d5814bb..595c91ec 100644
--- a/ash/system/notification_center/message_center_controller.cc
+++ b/ash/system/notification_center/message_center_controller.cc
@@ -125,10 +125,6 @@
 }
 
 MessageCenterController::~MessageCenterController() {
-  for (auto& observer : observers_) {
-    observer.OnArcNotificationInitializerDestroyed(this);
-  }
-
   // These members all depend on the MessageCenter instance, so must be
   // destroyed first.
   all_popup_blocker_.reset();
@@ -142,10 +138,10 @@
   message_center::MessageCenter::Shutdown();
 }
 
-void MessageCenterController::SetArcNotificationManagerInstance(
-    std::unique_ptr<ArcNotificationManagerBase> manager_instance) {
+void MessageCenterController::SetArcNotificationManager(
+    std::unique_ptr<ArcNotificationManagerBase> arc_notification_manager) {
   CHECK(!arc_notification_manager_);
-  arc_notification_manager_ = std::move(manager_instance);
+  arc_notification_manager_ = std::move(arc_notification_manager);
   arc_notification_manager_->Init(
       std::make_unique<ArcNotificationManagerDelegateImpl>(),
       Shell::Get()
@@ -155,12 +151,13 @@
       message_center::MessageCenter::Get());
 
   for (auto& observer : observers_) {
-    observer.OnSetArcNotificationsInstance(arc_notification_manager_.get());
+    observer.OnArcNotificationManagerInitialized(
+        arc_notification_manager_.get());
   }
 }
 
 ArcNotificationManagerBase*
-MessageCenterController::GetArcNotificationManagerInstance() {
+MessageCenterController::GetArcNotificationManager() {
   return arc_notification_manager_.get();
 }
 
diff --git a/ash/system/notification_center/message_center_controller.h b/ash/system/notification_center/message_center_controller.h
index 88a1ea4..b535fb0 100644
--- a/ash/system/notification_center/message_center_controller.h
+++ b/ash/system/notification_center/message_center_controller.h
@@ -44,9 +44,9 @@
   ~MessageCenterController() override;
 
   // ArcNotificationsHostInitializer:
-  void SetArcNotificationManagerInstance(
-      std::unique_ptr<ArcNotificationManagerBase> manager_instance) override;
-  ArcNotificationManagerBase* GetArcNotificationManagerInstance() override;
+  void SetArcNotificationManager(std::unique_ptr<ArcNotificationManagerBase>
+                                     arc_notification_manager) override;
+  ArcNotificationManagerBase* GetArcNotificationManager() override;
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
 
diff --git a/ash/system/power/power_notification_controller.cc b/ash/system/power/power_notification_controller.cc
index c077717..c976fdb 100644
--- a/ash/system/power/power_notification_controller.cc
+++ b/ash/system/power/power_notification_controller.cc
@@ -22,6 +22,7 @@
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/numerics/ranges.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/strcat.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -29,6 +30,7 @@
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/public/cpp/notification.h"
 #include "ui/message_center/public/cpp/notification_delegate.h"
+#include "ui/message_center/public/cpp/notification_types.h"
 
 using message_center::MessageCenter;
 using message_center::Notification;
@@ -160,6 +162,8 @@
 
 }  // namespace
 
+const char PowerNotificationController::kIncompatibleChargerNotificationId[] =
+    "incompatible-charger";
 const char PowerNotificationController::kUsbNotificationId[] = "usb-charger";
 
 PowerNotificationController::PowerNotificationController(
@@ -214,6 +218,10 @@
     return;
   }
 
+  if (ash::features::IsHybridChargerNotificationsEnabled()) {
+    MaybeShowIncompatibleChargerNotification();
+  }
+
   MaybeShowUsbChargerNotification();
   MaybeShowDualRoleNotification();
 
@@ -250,6 +258,8 @@
   }
 
   battery_was_full_ = PowerStatus::Get()->IsBatteryFull();
+  incompatible_charger_was_connected_ =
+      PowerStatus::Get()->IsIncompatibleChargerConnected();
   usb_charger_was_connected_ = PowerStatus::Get()->IsUsbChargerConnected();
   line_power_was_connected_ = PowerStatus::Get()->IsLinePowerConnected();
   remaining_time_to_empty_from_critical_state_ =
@@ -290,6 +300,50 @@
   shell_observation_.Reset();
 }
 
+bool PowerNotificationController::MaybeShowIncompatibleChargerNotification() {
+  const PowerStatus& status = *PowerStatus::Get();
+
+  const bool show = status.IsIncompatibleChargerConnected();
+
+  if (show && !incompatible_charger_was_connected_) {
+    message_center::RichNotificationData rich_notification_data;
+    // Insufficient charger notifications should display on fullscreen windows.
+    rich_notification_data.fullscreen_visibility =
+        message_center::FullscreenVisibility::OVER_USER;
+
+    const auto incompatible_charger_title = l10n_util::GetStringUTF16(
+        IDS_ASH_STATUS_TRAY_INCOMPATIBLE_CHARGER_TITLE);
+    const auto incompatible_charger_message = l10n_util::GetStringUTF16(
+        IDS_ASH_STATUS_TRAY_INCOMPATIBLE_CHARGER_MESSAGE);
+
+    std::unique_ptr<Notification> notification = CreateSystemNotificationPtr(
+        message_center::NOTIFICATION_TYPE_SIMPLE,
+        kIncompatibleChargerNotificationId, incompatible_charger_title,
+        incompatible_charger_message, std::u16string(), GURL(),
+        message_center::NotifierId(
+            message_center::NotifierType::SYSTEM_COMPONENT, kNotifierPower,
+            NotificationCatalogName::kIncompatibleCharger),
+        rich_notification_data, nullptr /*=delegate*/,
+        kNotificationLowPowerChargerIcon,
+        message_center::SystemNotificationWarningLevel::CRITICAL_WARNING);
+
+    notification->set_pinned(true);
+    notification->set_never_timeout(true);
+    notification->SetSystemPriority();
+
+    message_center_->AddNotification(std::move(notification));
+    return true;
+  }
+
+  if (!show && incompatible_charger_was_connected_) {
+    message_center_->RemoveNotification(kIncompatibleChargerNotificationId,
+                                        false);
+    return true;
+  }
+
+  return false;
+}
+
 bool PowerNotificationController::MaybeShowUsbChargerNotification() {
   const PowerStatus& status = *PowerStatus::Get();
 
diff --git a/ash/system/power/power_notification_controller.h b/ash/system/power/power_notification_controller.h
index c292ea6..b509872 100644
--- a/ash/system/power/power_notification_controller.h
+++ b/ash/system/power/power_notification_controller.h
@@ -116,6 +116,9 @@
   // Overridden from ash::ShellObserver:
   void OnShellDestroying() override;
 
+  // Show a notification when an incompatible charger is plugged in.
+  bool MaybeShowIncompatibleChargerNotification();
+
   // Shows a notification that a low-power USB charger has been connected.
   // Returns true if a notification was shown or explicitly hidden.
   bool MaybeShowUsbChargerNotification();
@@ -149,6 +152,7 @@
   // Reset the timestamp related to critical notification.
   void ResetCriticalNotificationTimestamp();
 
+  static const char kIncompatibleChargerNotificationId[];
   static const char kUsbNotificationId[];
 
   raw_ptr<PrefService> local_state_;                             // Unowned.
@@ -160,6 +164,10 @@
   // Was the battery full the last time OnPowerStatusChanged() was called?
   bool battery_was_full_ = false;
 
+  // Was an incompatible charger connected the last time OnPowerStatusChanged()
+  // was called?
+  bool incompatible_charger_was_connected_ = false;
+
   // Was a USB charger connected the last time OnPowerStatusChanged() was
   // called?
   bool usb_charger_was_connected_ = false;
diff --git a/ash/system/power/power_status.cc b/ash/system/power/power_status.cc
index 244a41a..905be45 100644
--- a/ash/system/power/power_status.cc
+++ b/ash/system/power/power_status.cc
@@ -228,7 +228,14 @@
 
 bool PowerStatus::IsLinePowerConnected() const {
   return proto_.external_power() !=
-         power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED;
+             power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED &&
+         !IsIncompatibleChargerConnected();
+}
+
+bool PowerStatus::IsIncompatibleChargerConnected() const {
+  return proto_.external_power() ==
+         power_manager::
+             PowerSupplyProperties_ExternalPower_LOW_VOLTAGE_NO_CHARGE;
 }
 
 bool PowerStatus::IsMainsChargerConnected() const {
@@ -422,7 +429,11 @@
   const std::optional<base::TimeDelta> time =
       IsBatteryCharging() ? GetBatteryTimeToFull() : GetBatteryTimeToEmpty();
 
-  if (IsUsbChargerConnected()) {
+  if (ash::features::IsHybridChargerNotificationsEnabled() &&
+      IsIncompatibleChargerConnected()) {
+    battery_time_accessible = l10n_util::GetStringUTF16(
+        IDS_ASH_STATUS_TRAY_BATTERY_INCOMPATIBLE_CHARGER_ACCESSIBLE);
+  } else if (IsUsbChargerConnected()) {
     battery_time_accessible = l10n_util::GetStringUTF16(
         IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE_ACCESSIBLE);
   } else if (IsBatteryTimeBeingCalculated()) {
diff --git a/ash/system/power/power_status.h b/ash/system/power/power_status.h
index 14fdd14..08ac78a6 100644
--- a/ash/system/power/power_status.h
+++ b/ash/system/power/power_status.h
@@ -210,6 +210,10 @@
   // Returns true if line power (including a charger of any type) is connected.
   virtual bool IsLinePowerConnected() const;
 
+  // Returns true if an incompatible charger is plugged in, which is incapable
+  // of charging the device at all (no trickle charging like with USB chargers).
+  bool IsIncompatibleChargerConnected() const;
+
   // Returns true if an official, non-USB charger is connected.
   bool IsMainsChargerConnected() const;
 
diff --git a/ash/system/video_conference/video_conference_tray_controller.cc b/ash/system/video_conference/video_conference_tray_controller.cc
index 8f0ac2b1..e330be50 100644
--- a/ash/system/video_conference/video_conference_tray_controller.cc
+++ b/ash/system/video_conference/video_conference_tray_controller.cc
@@ -507,7 +507,7 @@
   if (video_conference_manager_) {
     video_conference_manager_->SetSystemMediaDeviceStatus(
         crosapi::mojom::VideoConferenceMediaDevice::kCamera,
-        /*disabled=*/GetCameraMuted());
+        /*enabled=*/!GetCameraMuted());
   }
 
   // Attempt recording "Use while disabled" nudge action when camera is unmuted.
@@ -534,7 +534,7 @@
   if (video_conference_manager_) {
     video_conference_manager_->SetSystemMediaDeviceStatus(
         crosapi::mojom::VideoConferenceMediaDevice::kCamera,
-        /*disabled=*/GetCameraMuted());
+        /*enabled=*/!GetCameraMuted());
   }
 
   // Attempt recording "Use while disabled" nudge action when camera is unmuted.
@@ -570,7 +570,7 @@
   if (video_conference_manager_) {
     video_conference_manager_->SetSystemMediaDeviceStatus(
         crosapi::mojom::VideoConferenceMediaDevice::kMicrophone,
-        /*disabled=*/mute_on);
+        /*enabled=*/!mute_on);
   }
 
   microphone_muted_by_hardware_switch_ =
diff --git a/base/BUILD.gn b/base/BUILD.gn
index f96e257..5fefaf0b 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -4394,7 +4394,6 @@
       "allocator/partition_allocator/src/partition_alloc/pointers/raw_ref_nocompile.nc",
       "callback_list_nocompile.nc",
       "check_nocompile.nc",
-      "containers/adapters_nocompile.nc",
       "containers/buffer_iterator_nocompile.nc",
       "containers/checked_iterators_nocompile.nc",
       "containers/contains_nocompile.nc",
@@ -4439,6 +4438,11 @@
       "unsafe_buffers_nocompile.nc",
       "values_nocompile.nc",
     ]
+    if (use_cxx23) {
+      sources += [ "containers/adapters_nocompile_cpp23.nc" ]
+    } else {
+      sources += [ "containers/adapters_nocompile.nc" ]
+    }
     deps = [ ":base" ]
   }
 }
diff --git a/base/allocator/partition_alloc_support.cc b/base/allocator/partition_alloc_support.cc
index 4566ef4..239f04a2 100644
--- a/base/allocator/partition_alloc_support.cc
+++ b/base/allocator/partition_alloc_support.cc
@@ -1051,10 +1051,9 @@
 
   // Configure ASAN hooks to report the `MiraclePtr status`. This is enabled
   // only if BackupRefPtr is normally enabled in the current process for the
-  // current platform. Note that CastOS and iOS aren't protected by BackupRefPtr
+  // current platform. Note that CastOS is not protected by BackupRefPtr
   // a the moment, so they are excluded.
-#if PA_BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) && !PA_BUILDFLAG(IS_CASTOS) && \
-    !PA_BUILDFLAG(IS_IOS)
+#if PA_BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) && !PA_BUILDFLAG(IS_CASTOS)
   if (ShouldEnableFeatureOnProcess(
           base::features::kBackupRefPtrEnabledProcessesParam.Get(),
           process_type)) {
@@ -1070,7 +1069,7 @@
                                                EnableExtractionCheck(false),
                                                EnableInstantiationCheck(false));
   }
-#endif  // PA_BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
+#endif  // PA_BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) && !PA_BUILDFLAG(IS_CASTOS)
 
 #if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
   auto bucket_distribution = allocator_shim::BucketDistribution::kNeutral;
diff --git a/base/allocator/partition_allocator/partition_alloc.gni b/base/allocator/partition_allocator/partition_alloc.gni
index 957bec6..e4bdfa2 100644
--- a/base/allocator/partition_allocator/partition_alloc.gni
+++ b/base/allocator/partition_allocator/partition_alloc.gni
@@ -260,7 +260,7 @@
   # outside of Chromium.
   use_asan_backup_ref_ptr =
       build_with_chromium && is_asan &&
-      (is_win || is_android || is_linux || is_mac || is_chromeos)
+      (is_win || is_android || is_linux || is_apple || is_chromeos)
 
   # Use probe-on-destruct unowned ptr detection with ASAN.
   use_raw_ptr_asan_unowned_impl = false
diff --git a/base/android/java/src/org/chromium/base/TraceEvent.java b/base/android/java/src/org/chromium/base/TraceEvent.java
index 82a29d2..4244de9 100644
--- a/base/android/java/src/org/chromium/base/TraceEvent.java
+++ b/base/android/java/src/org/chromium/base/TraceEvent.java
@@ -319,8 +319,11 @@
         // by other applications
         if (sEnabled != enabled) {
             sEnabled = enabled;
-            ThreadUtils.getUiThreadLooper()
-                    .setMessageLogging(enabled ? LooperMonitorHolder.sInstance : null);
+            // UI Thread may not be set by this point.
+            if (sUiThreadReady) {
+                ThreadUtils.getUiThreadLooper()
+                        .setMessageLogging(enabled ? LooperMonitorHolder.sInstance : null);
+            }
         }
 
         if (sEnabled) {
@@ -355,7 +358,10 @@
             EarlyTraceEvent.maybeEnableInBrowserProcess();
         }
         if (EarlyTraceEvent.enabled()) {
-            ThreadUtils.getUiThreadLooper().setMessageLogging(LooperMonitorHolder.sInstance);
+            // UI Thread may not be set by this point.
+            if (sUiThreadReady) {
+                ThreadUtils.getUiThreadLooper().setMessageLogging(LooperMonitorHolder.sInstance);
+            }
         }
     }
 
@@ -368,6 +374,7 @@
         sUiThreadReady = true;
         if (sEnabled) {
             ViewHierarchyDumper.updateEnabledState();
+            ThreadUtils.getUiThreadLooper().setMessageLogging(LooperMonitorHolder.sInstance);
         }
     }
 
diff --git a/base/containers/adapters_nocompile_cpp23.nc b/base/containers/adapters_nocompile_cpp23.nc
new file mode 100644
index 0000000..9cd7882
--- /dev/null
+++ b/base/containers/adapters_nocompile_cpp23.nc
@@ -0,0 +1,27 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a "No Compile Test" suite.
+// http://dev.chromium.org/developers/testing/no-compile-tests
+
+#include "base/containers/adapters.h"
+#include "base/containers/span.h"
+
+#include <utility>
+#include <vector>
+
+namespace base {
+
+void RangeAsRvaluesRequiresNonBorrowedRange() {
+  std::vector<int> v;
+  RangeAsRvalues(v);  // expected-error {{no matching function for call to 'RangeAsRvalues'}}
+}
+
+void RangeAsRvaluesRequiresMutableRange() {
+  // A non-mutable range can't be moved from.
+  const std::vector<int> v;
+  RangeAsRvalues(std::move(v));  // expected-error {{no matching function for call to 'RangeAsRvalues'}}
+}
+
+}  // namespace base
diff --git a/base/features.cc b/base/features.cc
index d926d8e..e835924 100644
--- a/base/features.cc
+++ b/base/features.cc
@@ -210,7 +210,6 @@
 #endif
 
 #if BUILDFLAG(IS_APPLE)
-  File::InitializeFeatures();
   MessagePumpCFRunLoopBase::InitializeFeatures();
 
 // Kqueue is not used for ios blink.
diff --git a/base/files/file_posix.cc b/base/files/file_posix.cc
index 8dc26962..94caf9f 100644
--- a/base/files/file_posix.cc
+++ b/base/files/file_posix.cc
@@ -53,26 +53,6 @@
 
 namespace {
 
-#if BUILDFLAG(IS_APPLE)
-// When enabled, `F_FULLFSYNC` is not used in `File::Flush`. Instead,
-// `F_BARRIERFSYNC` or `flush()` is used (depending on the
-// "MacEfficientFileFlushUseBarrier" param). See
-// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fsync.2.html
-BASE_FEATURE(kMacEfficientFileFlush, base::FEATURE_ENABLED_BY_DEFAULT);
-
-const FeatureParam<bool> kMacEfficientFileFlushUseBarrier{
-    &kMacEfficientFileFlush, "MacEfficientFileFlushUseBarrier", true};
-
-enum class MacFileFlushMechanism {
-  kFlush,
-  kFullFsync,
-  kBarrierFsync,
-};
-
-std::atomic<MacFileFlushMechanism> g_mac_file_flush_mechanism{
-    MacFileFlushMechanism::kFullFsync};
-#endif  // BUILDFLAG(IS_APPLE)
-
 #if BUILDFLAG(IS_ANDROID)
 #define OffsetType off64_t
 // In case __USE_FILE_OFFSET64 is not used, the `File` methods in this file need
@@ -533,22 +513,6 @@
   return File(std::move(other_fd), async());
 }
 
-#if BUILDFLAG(IS_APPLE)
-void File::InitializeFeatures() {
-  if (FeatureList::IsEnabled(kMacEfficientFileFlush)) {
-    // "relaxed" because there is no dependency between these memory operations
-    // and other memory operations.
-    if (kMacEfficientFileFlushUseBarrier.Get()) {
-      g_mac_file_flush_mechanism.store(MacFileFlushMechanism::kBarrierFsync,
-                                       std::memory_order_relaxed);
-    } else {
-      g_mac_file_flush_mechanism.store(MacFileFlushMechanism::kFlush,
-                                       std::memory_order_relaxed);
-    }
-  }
-}
-#endif  // BUILDFLAG(IS_APPLE)
-
 // Static.
 File::Error File::OSErrorToFileError(int saved_errno) {
   switch (saved_errno) {
@@ -700,40 +664,25 @@
   // On macOS and iOS, fsync() is guaranteed to send the file's data to the
   // underlying storage device, but may return before the device actually writes
   // the data to the medium. When used by database systems, this may result in
-  // unexpected data loss. Depending on experiment state, this function may use
-  // F_BARRIERFSYNC or F_FULLFSYNC to provide stronger guarantees than fsync().
+  // unexpected data loss. This function uses F_BARRIERFSYNC to provide stronger
+  // guarantees than fsync(). The default behavior used to be `F_FULLFSYNC`.
+  // Changing it to F_BARRIERFSYNC for greatly reduced latency was extensively
+  // tried via experiment and showed no detectable sign of increased corruption
+  // in mechanisms that make use of this function. For similar discussions
+  // regarding rationale one can refer to the SQLite documentation where the
+  // default is to go directly to fsync. (See PRAGMA fullfsync)
   //
   // See documentation:
   // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fsync.2.html
   //
-  // "relaxed" because there is no dependency between this memory operation and
-  // other memory operations.
-  switch (g_mac_file_flush_mechanism.load(std::memory_order_relaxed)) {
-    case MacFileFlushMechanism::kBarrierFsync: {
-      if (!HANDLE_EINTR(fcntl(file_.get(), F_BARRIERFSYNC))) {
-        return true;
-      }
-      // Fall back to `fsync()` in case of failure.
-      break;
-    }
-    case MacFileFlushMechanism::kFullFsync: {
-      if (!HANDLE_EINTR(fcntl(file_.get(), F_FULLFSYNC))) {
-        return true;
-      }
-      // Fall back to `fsync()` in case of failure.
-      break;
-    }
-    case MacFileFlushMechanism::kFlush: {
-      // Fall back to `fsync()`.
-      break;
-    }
+  if (!HANDLE_EINTR(fcntl(file_.get(), F_BARRIERFSYNC))) {
+    return true;
   }
 
-  // `fsync()` if `F_BARRIERFSYNC` or `F_FULLFSYNC` failed, or if the mechanism
-  // is `kFlush`. Some file systems do not support `F_FULLFSYNC` /
+  // `fsync()` if `F_BARRIERFSYNC` failed. Some file systems do not support
   // `F_BARRIERFSYNC` but we cannot use the error code as a definitive indicator
-  // that it's the case, so we'll keep trying `F_FULLFSYNC` / `F_BARRIERFSYNC`
-  // for every call to this method when it's the case. See the CL description at
+  // that it's the case, so we'll keep trying `F_BARRIERFSYNC` for every call to
+  // this method when it's the case. See the CL description at
   // https://crrev.com/c/1400159 for details.
   return !HANDLE_EINTR(fsync(file_.get()));
 #else
diff --git a/base/memory_coordinator/memory_consumer_registry.cc b/base/memory_coordinator/memory_consumer_registry.cc
index 04317bf9..221568c 100644
--- a/base/memory_coordinator/memory_consumer_registry.cc
+++ b/base/memory_coordinator/memory_consumer_registry.cc
@@ -28,6 +28,11 @@
     : memory_consumer_(memory_consumer) {}
 
 // static
+bool MemoryConsumerRegistry::Exists() {
+  return g_memory_consumer_registry;
+}
+
+// static
 MemoryConsumerRegistry& MemoryConsumerRegistry::Get() {
   CHECK(g_memory_consumer_registry);
   return *g_memory_consumer_registry;
diff --git a/base/memory_coordinator/memory_consumer_registry.h b/base/memory_coordinator/memory_consumer_registry.h
index bf6a0d9d..c3d06776 100644
--- a/base/memory_coordinator/memory_consumer_registry.h
+++ b/base/memory_coordinator/memory_consumer_registry.h
@@ -38,6 +38,7 @@
 // global registry for the current process.
 class BASE_EXPORT MemoryConsumerRegistry {
  public:
+  static bool Exists();
   static MemoryConsumerRegistry& Get();
   static void Set(MemoryConsumerRegistry* instance);
 
diff --git a/base/numerics/byte_conversions.h b/base/numerics/byte_conversions.h
index 440b507..8400afb6 100644
--- a/base/numerics/byte_conversions.h
+++ b/base/numerics/byte_conversions.h
@@ -23,7 +23,7 @@
 // TODO(pkasting): Once C++23 is available, replace with std::byteswap.
 template <class T>
   requires(std::is_integral_v<T>)
-inline constexpr T ByteSwap(T value) {
+[[nodiscard]] inline constexpr T ByteSwap(T value) {
   return internal::SwapBytes(value);
 }
 
diff --git a/base/process/launch_unittest_win.cc b/base/process/launch_unittest_win.cc
index 60c53ad..0026667 100644
--- a/base/process/launch_unittest_win.cc
+++ b/base/process/launch_unittest_win.cc
@@ -86,6 +86,50 @@
   ASSERT_EQ(final_status, TERMINATION_STATUS_STILL_RUNNING);
 }
 
+TEST(LaunchWinTest, GetAppOutputWithExitCodeAndTimeout_InvalidApplication) {
+  CommandLine cl(FilePath(FILE_PATH_LITERAL("this-is-an-invalid-application")));
+  std::string output;
+  int exit_code = 0;
+  TerminationStatus final_status = TERMINATION_STATUS_MAX_ENUM;
+  int count = 0;
+  base::LaunchOptions options;
+  options.start_hidden = true;
+  ASSERT_FALSE(GetAppOutputWithExitCodeAndTimeout(
+      cl.GetCommandLineString(), true, &output, &exit_code, TimeDelta::Max(),
+      options,
+      [&](const Process& process, std::string_view partial_output) {
+        ASSERT_FALSE(process.IsValid());
+        ++count;
+      },
+      &final_status));
+  ASSERT_EQ(count, 0);
+  ASSERT_EQ(output, "");
+  ASSERT_EQ(final_status, TERMINATION_STATUS_LAUNCH_FAILED);
+}
+
+TEST(LaunchWinTest, GetAppOutputWithExitCodeAndTimeout_NoOutput) {
+  CommandLine cl(FilePath(FILE_PATH_LITERAL("cmd")));
+  cl.AppendArg("/q");
+  cl.AppendArg("/c");
+  std::string output;
+  int exit_code = 0;
+  TerminationStatus final_status = TERMINATION_STATUS_MAX_ENUM;
+  int count = 0;
+  base::LaunchOptions options;
+  options.start_hidden = true;
+  ASSERT_TRUE(GetAppOutputWithExitCodeAndTimeout(
+      cl.GetCommandLineString(), true, &output, &exit_code, TimeDelta::Max(),
+      options,
+      [&](const Process& process, std::string_view partial_output) {
+        ASSERT_TRUE(process.IsValid());
+        ++count;
+      },
+      &final_status));
+  ASSERT_EQ(count, 1);
+  ASSERT_EQ(output, "");
+  ASSERT_EQ(final_status, TERMINATION_STATUS_NORMAL_TERMINATION);
+}
+
 TEST(LaunchWinTest, GetAppOutputWithExitCodeAndTimeout_StreamingOutput) {
   CommandLine cl(FilePath(FILE_PATH_LITERAL("powershell")));
   cl.AppendArg("-command");
diff --git a/base/process/launch_win.cc b/base/process/launch_win.cc
index 96bee34..68dafcc 100644
--- a/base/process/launch_win.cc
+++ b/base/process/launch_win.cc
@@ -91,6 +91,7 @@
   if (!process.IsValid()) {
     return false;
   }
+  still_waiting(process, {});
 
   // Close our writing end of pipe now. Otherwise later read would not be able
   // to detect end of child's output.
@@ -140,14 +141,7 @@
       // The process ended, so continue reading as long as there is data
       // available.
     }
-
-    if (process_exited) {
-      // Exit and return from the function.
-      break;
-    }
-
-    still_waiting(process, {});
-  } while (timer.Elapsed() < timeout);
+  } while (!process_exited && (timer.Elapsed() < timeout));
 
   if (final_status) {
     *final_status = process_exited ? TERMINATION_STATUS_NORMAL_TERMINATION
diff --git a/build/config/c++/libc++.natvis b/build/config/c++/libc++.natvis
index 931da4a..9026f145 100644
--- a/build/config/c++/libc++.natvis
+++ b/build/config/c++/libc++.natvis
@@ -38,35 +38,45 @@
   <Type Name="std::__Cr::basic_string&lt;*&gt;">
     <Intrinsic Name="is_wide" Expression="sizeof($T1) > 1" />
     <Intrinsic Name="is_long" Expression="__rep_.__s.__is_long_" />
-    <Intrinsic Name="short_size" Expression="__rep_.__s.__size_" />
-    <Intrinsic Name="long_size" Expression="__rep_.__l.__size_" />
-    <Intrinsic Name="long_data" Expression="__rep_.__l.__data_" />
+    <Intrinsic Name="size" Expression="is_long() ? __rep_.__l.__size_ : (size_t)__rep_.__s.__size_" />
     <!-- Mask off the top bit which stores __is_long_. -->
     <Intrinsic Name="long_cap" Expression="__rep_.__l.__cap_ &amp; 0x7FFFFFFFFFFFFFFF" />
-    <!-- Short string capacity is buffer size minus 1 for null terminator -->
+    <!-- Short string capacity is buffer size minus 1 for null terminator. -->
     <Intrinsic Name="short_cap" Expression="sizeof(__rep_.__s.__data_) / sizeof($T1) - 1" />
+    <Intrinsic Name="data" Expression="is_long() ? __rep_.__l.__data_ : __rep_.__s.__data_" />
+    <Intrinsic Name="contains_embedded_nul" Expression="size() != (is_wide() ? wcslen(data()) : strlen(data()))" />
 
-    <DisplayString Condition="!is_wide() &amp;&amp; is_long()">{long_data(),[long_size()]s}</DisplayString>
-    <DisplayString Condition="!is_wide() &amp;&amp; !is_long()">{__rep_.__s.__data_,[short_size()]s}</DisplayString>
-    <DisplayString Condition="is_long()">{long_data(),[long_size()]su}</DisplayString>
-    <DisplayString Condition="!is_long()">{__rep_.__s.__data_,[short_size()]su}</DisplayString>
+    <DisplayString Condition="contains_embedded_nul() &amp;&amp; !is_wide()">{data(),[size()]s} (data after NUL exists)</DisplayString>
+    <DisplayString Condition="contains_embedded_nul()">{data(),[size()]su} (data after NUL exists)</DisplayString>
+    <DisplayString Condition="!is_wide()">{data(),[size()]s}</DisplayString>
+    <DisplayString>{data(),[size()]su}</DisplayString>
 
-    <StringView Condition="is_long()">long_data(),[long_size()]</StringView>
-    <StringView Condition="!is_long()">__rep_.__s.__data_,[short_size()]</StringView>
+    <StringView>data(),[size()]</StringView>
 
     <Expand>
-      <Item Name="[size]" Condition="is_long()" ExcludeView="simple">long_size()</Item>
-      <Item Name="[size]" Condition="!is_long()" ExcludeView="simple">(size_t)short_size()</Item>
+      <Item Name="[size]" ExcludeView="simple">size()</Item>
       <Item Name="[capacity]" Condition="is_long()" ExcludeView="simple">long_cap()</Item>
       <Item Name="[capacity]" Condition="!is_long()" ExcludeView="simple">short_cap()</Item>
-      <ArrayItems Condition="is_long()">
-        <Size>long_size()</Size>
-        <ValuePointer>long_data()</ValuePointer>
+      <Item Name="[data]" Condition="!is_long()" ExcludeView="simple">data()</Item>
+      <ArrayItems>
+        <Size>size()</Size>
+        <ValuePointer>data()</ValuePointer>
       </ArrayItems>
-      <ArrayItems Condition="!is_long()">
-        <Size>short_size()</Size>
-        <ValuePointer>__rep_.__s.__data_</ValuePointer>
-      </ArrayItems>
+      <!-- Offer to show nul-delimited substrings if there are any (the debugger will not show it at all otherwise). -->
+      <Synthetic Name="[nul-delimited substrings]" Condition="contains_embedded_nul()">
+        <Expand>
+          <CustomListItems>
+            <Variable Name="p" InitialValue="is_wide() ? (const wchar*)data() : (const char*)data()" />
+            <Variable Name="end" InitialValue="p + (size())" />
+            <Loop>
+              <Break Condition="p &gt;= end" />
+              <Item>p</Item>
+              <!-- +1 to advance past the nul character. -->
+              <Exec>p += (is_wide() ? wcslen(p) : strlen(p)) + 1</Exec>
+            </Loop>
+          </CustomListItems>
+        </Expand>
+      </Synthetic>
     </Expand>
   </Type>
 
@@ -427,8 +437,6 @@
 
   <Type Name="std::__Cr::unordered_map&lt;*&gt;">
     <AlternativeType Name="std::__Cr::unordered_multimap&lt;*&gt;" />
-    <AlternativeType Name="std::__Cr::unordered_multiset&lt;*&gt;" />
-    <AlternativeType Name="std::__Cr::unordered_set&lt;*&gt;" />
     <Intrinsic Name="size" Expression="__table_.__size_" />
     <Intrinsic Name="bucket_count" Expression="__table_.__bucket_list_.__deleter_.__size_" />
     <Intrinsic Name="max_load_factor" Expression="__table_.__max_load_factor_" />
@@ -438,22 +446,35 @@
       <Item Name="[bucket_count]">bucket_count()</Item>
       <Item Name="[load_factor]">bucket_count() != 0 ? (float)size() / bucket_count() : 0.f</Item>
       <Item Name="[max_load_factor]">max_load_factor()</Item>
-      <!-- Use CustomListItems instead of LinkedListItems because we
-        need to cast to __table::__node_pointer and LinkedListItems
-        evaluates <Value> in the context of the node, not of the container,
-        so we'd have to say std::unordered_map<$T1,...>::__table::__node_pointer
-        and then we couldn't share this <Type> between unordered_(multi)map
-        and unordered_(multi)set. -->
-      <CustomListItems>
-        <Variable Name="node" InitialValue="__table_.__first_node_.__next_" />
+      <LinkedListItems>
         <Size>size()</Size>
-        <Loop>
-          <Item>((__table::__node_pointer)node)-&gt;__value_</Item>
-          <Exec>node = node-&gt;__next_</Exec>
-        </Loop>
-      </CustomListItems>
+        <HeadPointer>__table_.__first_node_.__next_</HeadPointer>
+        <NextPointer>__next_</NextPointer>
+        <ValueNode>((std::__Cr::__hash_node&lt;std::__Cr::__hash_value_type&lt;$T1,$T2&gt;,void *&gt; *)this)-&gt;__value_</ValueNode>
+      </LinkedListItems>
     </Expand>
   </Type>
+
+  <Type Name="std::__Cr::unordered_set&lt;*&gt;">
+    <AlternativeType Name="std::__Cr::unordered_multiset&lt;*&gt;" />
+    <Intrinsic Name="size" Expression="__table_.__size_" />
+    <Intrinsic Name="bucket_count" Expression="__table_.__bucket_list_.__deleter_.__size_" />
+    <Intrinsic Name="max_load_factor" Expression="__table_.__max_load_factor_" />
+    <DisplayString>{{ size={size()} }}</DisplayString>
+    <Expand>
+      <Item Name="[size]">size()</Item>
+      <Item Name="[bucket_count]">bucket_count()</Item>
+      <Item Name="[load_factor]">bucket_count() != 0 ? (float)size() / bucket_count() : 0.f</Item>
+      <Item Name="[max_load_factor]">max_load_factor()</Item>
+      <LinkedListItems>
+        <Size>size()</Size>
+        <HeadPointer>__table_.__first_node_.__next_</HeadPointer>
+        <NextPointer>__next_</NextPointer>
+        <ValueNode>((std::__Cr::__hash_node&lt;$T1,void *&gt; *)this)-&gt;__value_</ValueNode>
+      </LinkedListItems>
+    </Expand>
+  </Type>
+
   <!-- This is the node __value_ of an unordered_(multi)map. Expand it through
     a separate formatter instead of in the <Item> expression above so that the
     same <Type> works for unordered_(multi)set and unordered_(multi)map. -->
diff --git a/build/config/siso/rust.star b/build/config/siso/rust.star
index 9656b4f..5d9a629 100644
--- a/build/config/siso/rust.star
+++ b/build/config/siso/rust.star
@@ -220,7 +220,7 @@
             "handler": "rust_link_handler",
             "deps": "none",  # disable gcc scandeps
             "remote": remote_link,
-            # "canonicalize_dir": True,  # TODO(b/300352286)
+            "canonicalize_dir": True,
             "timeout": "2m",
             "platform_ref": platform_ref,
         },
@@ -232,7 +232,7 @@
             "handler": "rust_link_handler",
             "deps": "none",  # disable gcc scandeps
             "remote": remote_link,
-            # "canonicalize_dir": True,  # TODO(b/300352286)
+            "canonicalize_dir": True,
             "timeout": "2m",
             "platform_ref": platform_ref,
         },
@@ -243,7 +243,7 @@
             "indirect_inputs": rust_indirect_inputs,
             "handler": "rust_link_handler",
             "deps": "none",  # disable gcc scandeps
-            # "canonicalize_dir": True,  # TODO(b/300352286)
+            "canonicalize_dir": True,
             "remote": remote_link,
             "timeout": "2m",
             "platform_ref": platform_ref,
@@ -255,7 +255,7 @@
             "indirect_inputs": rust_indirect_inputs,
             "deps": "none",  # disable gcc scandeps
             "remote": remote,
-            # "canonicalize_dir": True,  # TODO(b/300352286)
+            "canonicalize_dir": True,
             "timeout": "2m",
             "platform_ref": platform_ref,
         },
@@ -266,7 +266,7 @@
             "indirect_inputs": rust_indirect_inputs,
             "deps": "none",  # disable gcc scandeps
             "remote": remote,
-            # "canonicalize_dir": True,  # TODO(b/300352286)
+            "canonicalize_dir": True,
             "timeout": "2m",
             "platform_ref": platform_ref,
         },
@@ -279,7 +279,7 @@
             ],
             "handler": "rust_build_handler",
             "remote": remote and config.get(ctx, "cog"),
-            "input_root_absolute_path": True,
+            "canonicalize_dir": True,
             "timeout": "2m",
         },
         {
@@ -290,7 +290,7 @@
                 "third_party/rust-toolchain/lib/rustlib:rlib",
             ],
             "remote": remote and config.get(ctx, "cog"),
-            "input_root_absolute_path": True,
+            "canonicalize_dir": True,
             "timeout": "2m",
         },
         {
diff --git a/build/rust/gni_impl/rust_target.gni b/build/rust/gni_impl/rust_target.gni
index 947dfaf..f109442 100644
--- a/build/rust/gni_impl/rust_target.gni
+++ b/build/rust/gni_impl/rust_target.gni
@@ -65,6 +65,7 @@
     _crate_name = string_replace(_crate_name, "/", "_s")
     _crate_name = string_replace(_crate_name, ":", "_c")
     _crate_name = string_replace(_crate_name, "-", "_d")
+
     # The string replacements below are introduced to decrease the number
     # of characters in the crate name. This is particularly important while
     # building Chromium with lld_emit_indexes_and_imports=True, since it
@@ -230,8 +231,7 @@
   if (defined(invoker.cxx_bindings)) {
     _cxx_bindings = invoker.cxx_bindings
   }
-  _rustenv = [ "OUT_DIR=" +
-               rebase_path(_env_out_dir, get_path_info(_crate_root, "dir")) ]
+  _rustenv = [ "OUT_DIR=" + rebase_path(_env_out_dir, root_build_dir) ]
   if (defined(invoker.rustenv)) {
     _rustenv += invoker.rustenv
   }
diff --git a/build/rust/gni_impl/rustc_wrapper.py b/build/rust/gni_impl/rustc_wrapper.py
index 3134d63..5e87cb5 100755
--- a/build/rust/gni_impl/rustc_wrapper.py
+++ b/build/rust/gni_impl/rustc_wrapper.py
@@ -82,7 +82,18 @@
 # using our clang toolchain. That will remove the need for most of this
 # script.
 
-FILE_RE = re.compile("[^:]+: (.+)")
+# Regex for a line that specifies inter-file dependencies in a `.d` file.
+#
+# The syntax for such files follows the Makefile syntax and a regex is not
+# necessarily the best way to parse the lines. But this is what we started
+# with and it worked reasonably well so far. The main known issue is that
+# spaces in filenames are not supported.
+#
+# Other notes:
+# * We rely on greediness of `*` and `+`
+# * "#" is rejected as the first character to reject lines like
+#   `# env-dep:OUT_DIR=foo/bar`.
+FILE_RE = re.compile("^([^# ][^ ]*):( .+)?$")
 
 
 # Equivalent of python3.9 built-in
@@ -92,6 +103,49 @@
   return text
 
 
+def normalize_path(path, abs_build_root):
+  """Returns normalized `path` ('/' + relative to build root). """
+
+  # str.removeprefix() does not exist before python 3.9.
+  def remove_prefix(text, prefix):
+    if text.startswith(prefix):
+      return text[len(prefix):]
+    return text
+
+  return os.path.relpath(os.path.normpath(remove_prefix(
+      path, abs_build_root))).replace('\\', '/')
+
+
+def normalize_depline(depline, abs_build_root):
+  """Returns `depline` with normalized file paths.
+
+     Path normalization is needed to avoid "absolute path in deps ... request is
+     not relocatable" error.
+
+     If `depline` doesn't describe file/path dependencies (e.g. describes an
+     `env-dep`) then an unmodified `depline` is returned.
+  """
+  m = FILE_RE.match(depline)
+  if not m:
+    return depline
+
+  lhs_file = m.group(1)
+  rhs_group = m.group(2)
+  if rhs_group:
+    rhs_files = rhs_group.split()
+  else:
+    rhs_files = []
+
+  lhs_file = normalize_path(lhs_file, abs_build_root)
+  rhs_files = [normalize_path(f, abs_build_root) for f in rhs_files]
+
+  if rhs_files:
+    rhs_files = " ".join(rhs_files)
+    return f"{lhs_file}: {rhs_files}"
+  else:
+    return f"{lhs_file}:"
+
+
 def verify_inputs(depline, sources, abs_build_root):
   """Verify everything used by rustc (found in `depline`) was specified in the
   GN build rule (found in `sources` or `inputs`).
@@ -103,22 +157,12 @@
   consumed.
   """
 
-  # str.removeprefix() does not exist before python 3.9.
-  def remove_prefix(text, prefix):
-    if text.startswith(prefix):
-      return text[len(prefix):]
-    return text
-
-  def normalize_path(p):
-    return os.path.relpath(os.path.normpath(remove_prefix(
-        p, abs_build_root))).replace('\\', '/')
-
   # Collect the files that rustc says are needed.
   found_files = {}
   m = FILE_RE.match(depline)
-  if m:
-    files = m.group(1)
-    found_files = {normalize_path(f): f for f in files.split()}
+  if m and m.group(2):
+    files = m.group(2)
+    found_files = {normalize_path(f, abs_build_root): f for f in files.split()}
   # Get which ones are not listed in GN.
   missing_files = found_files.keys() - sources
 
@@ -190,10 +234,17 @@
   fixed_env_vars = []
   for item in rustenv:
     (k, v) = item.split("=", 1)
-    # TODO(https://crbug.com/442128549): remove once SDKROOT is no longer
-    # required to be absolute, or if we can pass an -isysroot equivalent flag
-    # instead.
-    if k == 'SDKROOT':
+
+    # Paths need to be relative at gn/ninja level (for compatibility with
+    # distributed builds), but it's okay to use absolute paths below gn/ninja
+    # level.  And because some paths need to be absolute, we make all of them
+    # absolute.  Examples of environment-variable-stored paths that need to be
+    # absolute:
+    #
+    # * `OUT_DIR` (because `rustc` resolves `include!` in relation to `.rs`
+    #   files - see https://crbug.com/448040713#comment6).
+    # * `SDKROOT` - see https://crbug.com/442128549
+    if os.path.exists(v):
       v = os.path.abspath(v)
     env[k] = v
     fixed_env_vars.append(k)
@@ -220,7 +271,9 @@
       if m and m.group(1) in fixed_env_vars:
         dirty = True  # We want to skip this line.
       else:
-        final_depfile_lines.append(line)
+        new_line = normalize_depline(line, abs_build_root)
+        dirty = dirty or (new_line != line)
+        final_depfile_lines.append(new_line)
 
   # Verify each dependent file is listed in sources/inputs.
   for line in final_depfile_lines:
diff --git a/build/rust/std/find_std_rlibs.py b/build/rust/std/find_std_rlibs.py
index 386258f..a9d90466 100755
--- a/build/rust/std/find_std_rlibs.py
+++ b/build/rust/std/find_std_rlibs.py
@@ -51,6 +51,7 @@
   if args.target:
     rustc_args.extend(["--target", args.target])
   rustlib_dir = subprocess.check_output(rustc_args).rstrip().decode()
+  rustlib_dir = os.path.relpath(rustlib_dir)
 
   # Copy the rlibs to a predictable location. Whilst we're doing so,
   # also write a .d file so that ninja knows it doesn't need to do this
diff --git a/build_overrides/partition_alloc.gni b/build_overrides/partition_alloc.gni
index 8d824e1..3c6b40e 100644
--- a/build_overrides/partition_alloc.gni
+++ b/build_overrides/partition_alloc.gni
@@ -65,12 +65,6 @@
   partition_alloc_add_configs = [ "//build/config/compiler:no_optimize" ]
 }
 
-# llvm_profile_set_target() generated by -fgenerate-profile invokes malloc()
-# internally. Since allocator_shim and PartitionAlloc are not reenterant,
-# the code will cause crashes. See crbug.com/338094768.
-partition_alloc_remove_configs +=
-    [ "//build/config/compiler/pgo:default_pgo_flags" ]
-
 # - Component build support is disabled on all platforms except Linux. It is
 #   known to cause issues on some (e.g. Windows with shims, Android with
 #   non-universal symbol wrapping), and has not been validated on others.
diff --git a/build_overrides/pdfium.gni b/build_overrides/pdfium.gni
index 0803c28..00d1d81 100644
--- a/build_overrides/pdfium.gni
+++ b/build_overrides/pdfium.gni
@@ -28,3 +28,7 @@
 # Disallow PDFium Fontations support.
 # TODO(crbug.com/pdfium/2107): Enable this.
 pdf_enable_fontations_override = false
+
+# Disallow PDFium Rust-based PNG decoder and encoder.
+# TODO(https://crbug.com/444045690): Enable this.
+pdf_enable_rust_png_override = false
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni
index 0cca5d5..04f1796 100644
--- a/buildtools/deps_revisions.gni
+++ b/buildtools/deps_revisions.gni
@@ -5,5 +5,5 @@
 declare_args() {
   # Used to cause full rebuilds on libc++ rolls. This should be kept in sync
   # with the libcxx_revision var in //DEPS.
-  libcxx_revision = "cc5f928de999c9c86202d7eb1d0f8bdc3b53465f"
+  libcxx_revision = "4b6389141910f2bb5df4574b6e0e9ec5a1acb165"
 }
diff --git a/buildtools/reclient_cfgs/configure_reclient_cfgs.py b/buildtools/reclient_cfgs/configure_reclient_cfgs.py
index 128bda2..8779d460 100755
--- a/buildtools/reclient_cfgs/configure_reclient_cfgs.py
+++ b/buildtools/reclient_cfgs/configure_reclient_cfgs.py
@@ -273,31 +273,21 @@
             return 1
 
     if args.skip_remoteexec_cfg_fetch:
-        return 0
-
-    logging.info("fetch reclient_cfgs for RBE project %s..." % rbe_project)
-
-    cipd_prefix = posixpath.join(args.cipd_prefix, rbe_project)
-
-    # cleanup unused nacl configs. preserve nacl/rewrapper_linux.cfg
-    # TODO(b/354804085): remove this code.
-    for cfg in [
-            "nacl/rewrapper_mac.cfg",
-            "nacl/rewrapper_windows.cfg",
-            "nacl/win-cross",
-    ]:
-        cfgpath = os.path.join(THIS_DIR, cfg)
-        if os.path.exists(cfgpath):
-            if os.path.isdir(cfgpath):
+        # remove stale reclient cfgs
+        for cfg in ["chromium-browser-clang", "python", "win-cross"]:
+            cfgpath = os.path.join(THIS_DIR, cfg)
+            if os.path.exists(cfgpath):
 
                 def onerror(func, cfgpath, exc_info):
                     os.chmod(cfgpath, 0o644)
                     func(cfgpath)
 
                 shutil.rmtree(cfgpath, onerror=onerror)
-            else:
-                os.chmod(cfgpath, 0o644)
-                os.remove(cfgpath)
+        return 0
+
+    logging.info("fetch reclient_cfgs for RBE project %s..." % rbe_project)
+
+    cipd_prefix = posixpath.join(args.cipd_prefix, rbe_project)
 
     tool_revisions = {
         "chromium-browser-clang": ClangRevision(),
diff --git a/buildtools/third_party/libc++/libcxx_headers.gni b/buildtools/third_party/libc++/libcxx_headers.gni
index 8672f406..ea76271 100644
--- a/buildtools/third_party/libc++/libcxx_headers.gni
+++ b/buildtools/third_party/libc++/libcxx_headers.gni
@@ -11,7 +11,7 @@
 import("//buildtools/deps_revisions.gni")
 
 assert(
-    libcxx_revision == "cc5f928de999c9c86202d7eb1d0f8bdc3b53465f",
+    libcxx_revision == "4b6389141910f2bb5df4574b6e0e9ec5a1acb165",
     "libcxx_headers.gni and third_party/libc++ are out of sync.$0x0A$0x0AIf you were messing around with the libc++ repository, run:$0x0A`buildtools/third_party/libc++/generate_libcxx_headers.py`$0x0A$0x0AIf the script doesn't resolve the error, file a bug to msta@ with reproduction details.$0x0A")
 
 libcxx_headers = [
@@ -856,6 +856,7 @@
   "//third_party/libc++/src/include/__type_traits/is_floating_point.h",
   "//third_party/libc++/src/include/__type_traits/is_function.h",
   "//third_party/libc++/src/include/__type_traits/is_fundamental.h",
+  "//third_party/libc++/src/include/__type_traits/is_generic_transparent_comparator.h",
   "//third_party/libc++/src/include/__type_traits/is_implicit_lifetime.h",
   "//third_party/libc++/src/include/__type_traits/is_implicitly_default_constructible.h",
   "//third_party/libc++/src/include/__type_traits/is_integral.h",
@@ -898,6 +899,7 @@
   "//third_party/libc++/src/include/__type_traits/make_32_64_or_128_bit.h",
   "//third_party/libc++/src/include/__type_traits/make_const_lvalue_ref.h",
   "//third_party/libc++/src/include/__type_traits/make_signed.h",
+  "//third_party/libc++/src/include/__type_traits/make_transparent.h",
   "//third_party/libc++/src/include/__type_traits/make_unsigned.h",
   "//third_party/libc++/src/include/__type_traits/maybe_const.h",
   "//third_party/libc++/src/include/__type_traits/nat.h",
diff --git a/cc/base/features.cc b/cc/base/features.cc
index 8bb7690..defaee5b2 100644
--- a/cc/base/features.cc
+++ b/cc/base/features.cc
@@ -206,6 +206,9 @@
 BASE_FEATURE(kOverscrollBehaviorRespectedOnAllScrollContainers,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kOverscrollEffectOnNonRootScrollers,
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kSkipFinishDuringReleaseLayerTreeFrameSink,
              base::FEATURE_ENABLED_BY_DEFAULT);
 
diff --git a/cc/base/features.h b/cc/base/features.h
index 36c68b8..83e32fa 100644
--- a/cc/base/features.h
+++ b/cc/base/features.h
@@ -222,6 +222,9 @@
 CC_BASE_EXPORT BASE_DECLARE_FEATURE(
     kOverscrollBehaviorRespectedOnAllScrollContainers);
 
+// When enabled, the overscroll effect will display on non-root scrollers.
+CC_BASE_EXPORT BASE_DECLARE_FEATURE(kOverscrollEffectOnNonRootScrollers);
+
 // A kill switch in case skipping finish causes unexpected issues.
 CC_BASE_EXPORT BASE_DECLARE_FEATURE(kSkipFinishDuringReleaseLayerTreeFrameSink);
 
diff --git a/cc/paint/oop_pixeltest.cc b/cc/paint/oop_pixeltest.cc
index f21ac99..1f462d03 100644
--- a/cc/paint/oop_pixeltest.cc
+++ b/cc/paint/oop_pixeltest.cc
@@ -1348,7 +1348,6 @@
                   int srcY) override {
     return false;
   }
-  void FlushPendingSkiaOps() override {}
 
  private:
   gpu::Mailbox mailbox_;
diff --git a/cc/paint/paint_image.cc b/cc/paint/paint_image.cc
index aeafd6b4..f81c5dd 100644
--- a/cc/paint/paint_image.cc
+++ b/cc/paint/paint_image.cc
@@ -343,8 +343,6 @@
 }
 
 void PaintImage::FlushPendingSkiaOps() {
-  if (texture_backing_)
-    texture_backing_->FlushPendingSkiaOps();
 }
 
 gfx::Size PaintImage::GetSize(AuxImage aux_image) const {
diff --git a/cc/paint/texture_backing.h b/cc/paint/texture_backing.h
index 4a59fe1..a51e55a3 100644
--- a/cc/paint/texture_backing.h
+++ b/cc/paint/texture_backing.h
@@ -45,10 +45,6 @@
                           size_t dst_row_bytes,
                           int src_x,
                           int src_y) = 0;
-
-  // Force a flush of any pending skia work. Only supported if this
-  // TextureBacking wraps an SkImage.
-  virtual void FlushPendingSkiaOps() = 0;
 };
 
 }  // namespace cc
diff --git a/cc/tiles/gpu_image_decode_cache_unittest.cc b/cc/tiles/gpu_image_decode_cache_unittest.cc
index d1bc98a..a5a7d38 100644
--- a/cc/tiles/gpu_image_decode_cache_unittest.cc
+++ b/cc/tiles/gpu_image_decode_cache_unittest.cc
@@ -566,16 +566,12 @@
           transfer_cache_helper_.GetEntryAs<ServiceImageTransferCacheEntry>(id);
       original_uploaded_plane = image_entry->GetPlaneImage(i);
       ASSERT_TRUE(original_uploaded_plane);
-      auto plane_with_mips = SkImages::TextureFromImage(
-          context_provider()->GrContext(), original_uploaded_plane,
-          skgpu::Mipmapped::kYes);
       // In test frameworks, Skia is unable to generate mipmaps for A16 formats.
       if (original_uploaded_plane->colorType() == kA16_unorm_SkColorType ||
           original_uploaded_plane->colorType() == kA16_float_SkColorType) {
         break;
       }
-      ASSERT_TRUE(plane_with_mips);
-      EXPECT_EQ(should_have_mips, original_uploaded_plane == plane_with_mips);
+      EXPECT_EQ(original_uploaded_plane->hasMipmaps(), should_have_mips);
     }
   }
 
@@ -3296,11 +3292,7 @@
       CompareAllPlanesToMippedVersions(
           cache.get(), draw_image, transfer_cache_entry_id, should_have_mips);
     } else {
-      sk_sp<SkImage> image_with_mips = SkImages::TextureFromImage(
-          context_provider()->GrContext(), decoded_draw_image.image(),
-          skgpu::Mipmapped::kYes);
-      EXPECT_EQ(should_have_mips,
-                image_with_mips == decoded_draw_image.image());
+      EXPECT_EQ(decoded_draw_image.image()->hasMipmaps(), should_have_mips);
     }
     cache->DrawWithImageFinished(draw_image, decoded_draw_image);
     cache->UnrefImage(draw_image);
@@ -3368,11 +3360,7 @@
                                        transfer_cache_entry_id,
                                        false /* should_have_mips */);
     } else {
-      sk_sp<SkImage> image_with_mips = SkImages::TextureFromImage(
-          context_provider()->GrContext(), decoded_draw_image.image(),
-          skgpu::Mipmapped::kYes);
-      ASSERT_TRUE(image_with_mips);
-      EXPECT_NE(image_with_mips, decoded_draw_image.image());
+      EXPECT_FALSE(decoded_draw_image.image()->hasMipmaps());
     }
     cache->DrawWithImageFinished(draw_image, decoded_draw_image);
     cache->UnrefImage(draw_image);
@@ -3417,10 +3405,7 @@
                                        transfer_cache_entry_id,
                                        true /* should_have_mips */);
     } else {
-      sk_sp<SkImage> image_with_mips = SkImages::TextureFromImage(
-          context_provider()->GrContext(), decoded_draw_image.image(),
-          skgpu::Mipmapped::kYes);
-      EXPECT_EQ(image_with_mips, decoded_draw_image.image());
+      EXPECT_TRUE(decoded_draw_image.image()->hasMipmaps());
     }
     cache->DrawWithImageFinished(draw_image, decoded_draw_image);
     cache->UnrefImage(draw_image);
@@ -3473,10 +3458,7 @@
                                        transfer_cache_entry_id,
                                        false /* should_have_mips */);
     } else {
-      sk_sp<SkImage> image_with_mips = SkImages::TextureFromImage(
-          context_provider()->GrContext(), decoded_draw_image.image(),
-          skgpu::Mipmapped::kYes);
-      EXPECT_NE(image_with_mips, decoded_draw_image.image());
+      EXPECT_FALSE(decoded_draw_image.image()->hasMipmaps());
     }
     images_to_unlock.push_back({draw_image, decoded_draw_image});
   }
@@ -3515,10 +3497,7 @@
                                        transfer_cache_entry_id,
                                        true /* should_have_mips */);
     } else {
-      sk_sp<SkImage> image_with_mips = SkImages::TextureFromImage(
-          context_provider()->GrContext(), decoded_draw_image.image(),
-          skgpu::Mipmapped::kYes);
-      EXPECT_EQ(image_with_mips, decoded_draw_image.image());
+      EXPECT_TRUE(decoded_draw_image.image()->hasMipmaps());
     }
     images_to_unlock.push_back({draw_image, decoded_draw_image});
   }
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index d7deb1de..886e521 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -4818,8 +4818,12 @@
       const auto& scroll_tree = active_tree_->property_trees()->scroll_tree();
       if (const gfx::Rect* cull_rect =
               scroll_tree.ScrollingContentsCullRect(element_id)) {
-        if (const auto* scroll_node =
-                scroll_tree.FindNodeFromElementId(element_id)) {
+        const auto* scroll_node = scroll_tree.FindNodeFromElementId(element_id);
+        // The `transform_id` can be invalid when the scroll is starting. By
+        // definition there can be no checkerboarding yet, so we can skip
+        // this calculation.
+        if (scroll_node &&
+            scroll_node->transform_id != kInvalidPropertyNodeId) {
           gfx::RectF visible_rect(
               gfx::Rect(scroll_node->container_origin,
                         scroll_tree.container_bounds(scroll_node->id)));
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index d55153d..d2a2569c 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -876,23 +876,27 @@
 }
 
 const gfx::Transform& TransformTree::FromScreen(int node_id) const {
-  DCHECK(static_cast<int>(cached_data_.size()) > node_id);
+  DCHECK(static_cast<int>(cached_data_.size()) > node_id &&
+         node_id != kInvalidPropertyNodeId);
   return cached_data_[node_id].from_screen;
 }
 
 void TransformTree::SetFromScreen(int node_id,
                                   const gfx::Transform& transform) {
-  DCHECK(static_cast<int>(cached_data_.size()) > node_id);
+  DCHECK(static_cast<int>(cached_data_.size()) > node_id &&
+         node_id != kInvalidPropertyNodeId);
   cached_data_[node_id].from_screen = transform;
 }
 
 const gfx::Transform& TransformTree::ToScreen(int node_id) const {
-  DCHECK(static_cast<int>(cached_data_.size()) > node_id);
+  DCHECK(static_cast<int>(cached_data_.size()) > node_id &&
+         node_id != kInvalidPropertyNodeId);
   return cached_data_[node_id].to_screen;
 }
 
 void TransformTree::SetToScreen(int node_id, const gfx::Transform& transform) {
-  DCHECK(static_cast<int>(cached_data_.size()) > node_id);
+  DCHECK(static_cast<int>(cached_data_.size()) > node_id &&
+         node_id != kInvalidPropertyNodeId);
   cached_data_[node_id].to_screen = transform;
   cached_data_[node_id].is_showing_backface = transform.IsBackFaceVisible();
 }
diff --git a/chrome/VERSION b/chrome/VERSION
index 11f992f..a5d3181b 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=143
 MINOR=0
-BUILD=7454
+BUILD=7458
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 4dfe470..3806eeb 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -924,7 +924,6 @@
       "//chrome/browser/tabmodel/internal:java",
       "//chrome/browser/tabwindow/internal:java",
       "//chrome/browser/task_manager/internal/android:java",
-      "//chrome/browser/touch_to_fill/autofill/one_time_tokens/android/internal:java",
       "//chrome/browser/touch_to_fill/password_manager/android/internal:java",
       "//chrome/browser/touch_to_fill/password_manager/no_passkeys/internal/android:java",
       "//chrome/browser/touch_to_fill/password_manager/password_generation/android/internal:java",
@@ -1153,7 +1152,6 @@
       "//chrome/browser/tabwindow/internal:junit",
       "//chrome/browser/task_manager/internal/android:junit",
       "//chrome/browser/touch_to_fill/autofill/android/internal:junit",
-      "//chrome/browser/touch_to_fill/autofill/one_time_tokens/android/internal:junit",
       "//chrome/browser/touch_to_fill/common/android:junit",
       "//chrome/browser/touch_to_fill/password_manager/no_passkeys/internal/android:junit",
       "//chrome/browser/touch_to_fill/password_manager/password_generation/android/internal:junit",
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index 44d688f..ae08ce4 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -288,6 +288,7 @@
   "java/res/drawable-xxxhdpi/tabswitcher_border_frame_inner_shadow.9.png",
   "java/res/drawable-xxxhdpi/tabswitcher_border_frame_shadow.9.png",
   "java/res/drawable/adaptive_toolbar_preference_header.xml",
+  "java/res/drawable/add_link.xml",
   "java/res/drawable/arrow_back_24px.xml",
   "java/res/drawable/arrow_down.xml",
   "java/res/drawable/arrow_forward_24px.xml",
@@ -308,6 +309,7 @@
   "java/res/drawable/capture_overlay_border.xml",
   "java/res/drawable/card_container_background.xml",
   "java/res/drawable/checkerboard_background.xml",
+  "java/res/drawable/checklist.xml",
   "java/res/drawable/checkmark_selection_drawable.xml",
   "java/res/drawable/chrome_logo_on_circular_background.xml",
   "java/res/drawable/computer_black_24dp.xml",
@@ -410,6 +412,7 @@
   "java/res/drawable/price_tracking_enabled_outline.xml",
   "java/res/drawable/qr_code.xml",
   "java/res/drawable/reading_list_empty_state_illustration.xml",
+  "java/res/drawable/receipt_long.xml",
   "java/res/drawable/rtl_gesture_nav_iph_dialog_drawable.xml",
   "java/res/drawable/search_activity_bg.xml",
   "java/res/drawable/send_tab.xml",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index de3c7ad..07bcafc 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -982,6 +982,7 @@
   "java/src/org/chromium/chrome/browser/settings/SettingsIntentUtil.java",
   "java/src/org/chromium/chrome/browser/settings/SettingsNavigationImpl.java",
   "java/src/org/chromium/chrome/browser/settings/WideDisplayPadding.java",
+  "java/src/org/chromium/chrome/browser/settings/search/SettingsSearchCoordinator.java",
   "java/src/org/chromium/chrome/browser/share/ChromeCustomShareAction.java",
   "java/src/org/chromium/chrome/browser/share/LensUtils.java",
   "java/src/org/chromium/chrome/browser/share/ShareButtonController.java",
@@ -1139,6 +1140,7 @@
   "java/src/org/chromium/chrome/browser/tabmodel/TabPersistenceFileInfo.java",
   "java/src/org/chromium/chrome/browser/tabmodel/TabPersistencePolicy.java",
   "java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java",
+  "java/src/org/chromium/chrome/browser/tabmodel/TabPinnerActionListener.java",
   "java/src/org/chromium/chrome/browser/tabmodel/TabRemoverImpl.java",
   "java/src/org/chromium/chrome/browser/tabmodel/TabUngrouperImpl.java",
   "java/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicy.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
index 863f70d..bfd411f7 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
@@ -32,6 +32,7 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabFavicon;
@@ -43,7 +44,7 @@
 import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider.TabFaviconMetadata;
 import org.chromium.chrome.browser.tab_ui.ThumbnailProvider;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter;
-import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
+import org.chromium.chrome.browser.theme.ThemeModuleUtils;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.tab_groups.TabGroupColorId;
@@ -342,7 +343,7 @@
                 @Nullable Bitmap thumbnail, int index, boolean showGhostLoadIllustration) {
             final RectF rect = mThumbnailRects.get(index);
             if (thumbnail == null) {
-                if (SurfaceColorUpdateUtils.useNewGm3GtsTabGroupColors()) {
+                if (useNewGm3GtsTabGroupColors()) {
                     mTextPaint.setColor(mResolvedTextColor);
                     mColordEmptyThumbnailPaint.setColor(mResolvedEmptyPlaceholderColor);
                     Paint emptyThumbnailPaint =
@@ -358,7 +359,7 @@
 
                 if (showGhostLoadIllustration) {
                     Resources res = mContext.getResources();
-                    if (SurfaceColorUpdateUtils.useNewGm3GtsTabGroupColors()) {
+                    if (useNewGm3GtsTabGroupColors()) {
                         mEmptyThumbnailGhostLoadIllustration.setTint(
                                 mResolvedGhostIllustrationColor);
                     }
@@ -487,6 +488,12 @@
                         });
             }
         }
+
+        /** Whether new GM3 colors are being used for the tab group colors. */
+        public static boolean useNewGm3GtsTabGroupColors() {
+            return ChromeFeatureList.sAndroidTabGroupsColorUpdateGm3.isEnabled()
+                    || ThemeModuleUtils.isForceEnableDependencies();
+        }
     }
 
     public MultiThumbnailCardProvider(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContextMenuCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContextMenuCoordinator.java
index 431bb03d..1bddea86 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContextMenuCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContextMenuCoordinator.java
@@ -204,7 +204,7 @@
                 showTabListEditor.show(tab.getId());
                 recordUserActionWithPrefix("SelectTabs");
             } else if (menuId == R.id.pin_tab) {
-                tabModel.pinTab(tab.getId());
+                tabModel.pinTab(tab.getId(), /* showUngroupDialog= */ true);
                 recordUserActionWithPrefix("PinTab");
             } else if (menuId == R.id.unpin_tab) {
                 tabModel.unpinTab(tab.getId());
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContextMenuCoordinatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContextMenuCoordinatorUnitTest.java
index 14962f9b..dde228da 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContextMenuCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContextMenuCoordinatorUnitTest.java
@@ -298,7 +298,7 @@
                 TAB_ID,
                 /* collaborationId= */ null,
                 /* listViewTouchTracker= */ null);
-        verify(mTabModel).pinTab(TAB_ID);
+        verify(mTabModel).pinTab(TAB_ID, /* showUngroupDialog= */ true);
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java
index 4c99756..c68190e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java
@@ -23,6 +23,7 @@
 import org.chromium.chrome.browser.collaboration.messaging.MessagingBackendServiceFactory;
 import org.chromium.chrome.browser.data_sharing.DataSharingServiceFactory;
 import org.chromium.chrome.browser.data_sharing.DataSharingTabManager;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.hub.PaneManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileProvider;
@@ -33,7 +34,6 @@
 import org.chromium.chrome.browser.tab_ui.ActionConfirmationManager;
 import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter;
-import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeControllerFactory;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils;
@@ -220,6 +220,7 @@
     }
 
     private static boolean enableContainment() {
-        return SurfaceColorUpdateUtils.isTabGroupListContainmentEnabled();
+        return ChromeFeatureList.sGridTabSwitcherSurfaceColorUpdate.isEnabled()
+                && ChromeFeatureList.sTabGroupListContainment.getValue();
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index 86d5627..d15d04d 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -636,14 +636,21 @@
                 @Override
                 public void onTabPinnedStateChanged(Tab tab, boolean isPinned) {
                     int index = mModelList.indexFromTabId(tab.getId());
+                    updateTab(index, tab, /* isUpdatingId= */ false, /* quickMode= */ false);
+
+                    // When pinning a tab in a group it will be removed from the group so the index
+                    // update is unnecessary.
+                    if (!mActionsOnAllRelatedTabs) return;
+
                     int finalIndex =
                             mModelList.indexOfNthTabCard(
                                     mCurrentTabGroupModelFilterSupplier
                                             .get()
                                             .getTabModel()
                                             .indexOf(tab));
-                    updateTab(index, tab, /* isUpdatingId= */ false, /* quickMode= */ false);
-                    if (index != finalIndex) {
+                    if (index != finalIndex
+                            && index != TabModel.INVALID_TAB_INDEX
+                            && finalIndex != TabModel.INVALID_TAB_INDEX) {
                         mModelList.move(index, finalIndex);
                     }
                 }
diff --git a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_ar.xtb b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_ar.xtb
index 0c1718f..d17628f 100644
--- a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_ar.xtb
+++ b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_ar.xtb
@@ -26,6 +26,7 @@
 <translation id="1738346536511908887">{TAB_GROUPS_COUNT,plural, =1{لم تعُد مجموعة واحدة (<ph name="TAB_GROUPS_COUNT_ONE" />) لعلامات التبويب متوفّرة}zero{لم تعُد <ph name="TAB_GROUPS_COUNT_MANY" /> مجموعة لعلامات التبويب متوفّرة}two{لم تعُد مجموعتان (<ph name="TAB_GROUPS_COUNT_MANY" />) لعلامات التبويب متوفّرة}few{لم تعُد <ph name="TAB_GROUPS_COUNT_MANY" /> مجموعات لعلامات التبويب متوفّرة}many{لم تعُد <ph name="TAB_GROUPS_COUNT_MANY" /> مجموعة لعلامات التبويب متوفّرة}other{لم تعُد <ph name="TAB_GROUPS_COUNT_MANY" /> مجموعة لعلامات التبويب متوفّرة}}</translation>
 <translation id="176485176228926164"><ph name="BEGIN_LINK1" />مزيد من المعلومات<ph name="END_LINK1" /></translation>
 <translation id="1828808367831369073">تم تجميع علامة تبويب واحدة (<ph name="TABS_COUNT_ONE" />)</translation>
+<translation id="1844381456014878100">بعد 7 أيام من عدم النشاط</translation>
 <translation id="1869137256605757565">{TABS_COUNT,plural, =1{علامة تبويب واحدة (<ph name="TABS_COUNT_ONE" />)}zero{<ph name="TABS_COUNT_MANY" /> علامة تبويب}two{علامتا تبويب (<ph name="TABS_COUNT_MANY" />)}few{<ph name="TABS_COUNT_MANY" /> علامات تبويب}many{<ph name="TABS_COUNT_MANY" /> علامة تبويب}other{<ph name="TABS_COUNT_MANY" /> علامة تبويب}}</translation>
 <translation id="1909810060368225529">ملاحظة: يمكن للجميع الاطّلاع على التغييرات التي يتم إجراؤها على مجموعة علامات التبويب المشتركة هذه. يمكن للجميع إجراء تغييرات على مجموعات علامات التبويب المشتركة</translation>
 <translation id="1925363809167540686">{TAB_COUNT,plural, =1{تثبيت علامة تبويب واحدة}zero{تثبيت علامات التبويب}two{تثبيت علامتَي التبويب}few{تثبيت علامات التبويب}many{تثبيت علامات التبويب}other{تثبيت علامات التبويب}}</translation>
@@ -154,6 +155,7 @@
 <translation id="6784585810034073638">{TAB_COUNT,plural, =1{الإضافة إلى مجموعة باسم "<ph name="TITLE_OF_GROUP" />" تتضمّن علامة تبويب واحدة (<ph name="TAB_COUNT1" />)}zero{الإضافة إلى مجموعة باسم "<ph name="TITLE_OF_GROUP" />" تتضمّن <ph name="TAB_COUNT1" /> علامة تبويب}two{الإضافة إلى مجموعة باسم "<ph name="TITLE_OF_GROUP" />" تتضمّن علامتَي تبويب (<ph name="TAB_COUNT1" />)}few{الإضافة إلى مجموعة باسم "<ph name="TITLE_OF_GROUP" />" تتضمّن <ph name="TAB_COUNT1" /> علامات تبويب}many{الإضافة إلى مجموعة باسم "<ph name="TITLE_OF_GROUP" />" تتضمّن <ph name="TAB_COUNT1" /> علامة تبويب}other{الإضافة إلى مجموعة باسم "<ph name="TITLE_OF_GROUP" />" تتضمّن <ph name="TAB_COUNT1" /> علامة تبويب}}</translation>
 <translation id="6818926723028410516">اختيار عناصر</translation>
 <translation id="6856809498882026482">إخفاء وضع التحديد المتعدد</translation>
+<translation id="6957436309971790274">بعد 21 يومًا من عدم النشاط</translation>
 <translation id="7064815189603108598">{NUMBER_OF_TABS,plural, =1{تم توسيع مجموعة علامات التبويب "<ph name="TITLE_OF_GROUP" />" التي تمّت مشاركتها وتظهر بلون <ph name="COLOR_NAME" /> وتحتوي على علامة تبويب واحدة (<ph name="TABS_COUNT_ONE" />).}zero{تم توسيع مجموعة علامات التبويب "<ph name="TITLE_OF_GROUP" />" التي تمّت مشاركتها وتظهر بلون <ph name="COLOR_NAME" /> وتحتوي على <ph name="TABS_COUNT_MANY" /> علامة تبويب.}two{تم توسيع مجموعة علامات التبويب "<ph name="TITLE_OF_GROUP" />" التي تمّت مشاركتها وتظهر بلون <ph name="COLOR_NAME" /> وتحتوي على علامتَي تبويب (<ph name="TABS_COUNT_MANY" />).}few{تم توسيع مجموعة علامات التبويب "<ph name="TITLE_OF_GROUP" />" التي تمّت مشاركتها وتظهر بلون <ph name="COLOR_NAME" /> وتحتوي على <ph name="TABS_COUNT_MANY" /> علامات تبويب.}many{تم توسيع مجموعة علامات التبويب "<ph name="TITLE_OF_GROUP" />" التي تمّت مشاركتها وتظهر بلون <ph name="COLOR_NAME" /> وتحتوي على <ph name="TABS_COUNT_MANY" /> علامة تبويب.}other{تم توسيع مجموعة علامات التبويب "<ph name="TITLE_OF_GROUP" />" التي تمّت مشاركتها وتظهر بلون <ph name="COLOR_NAME" /> وتحتوي على <ph name="TABS_COUNT_MANY" /> علامة تبويب.}}</translation>
 <translation id="709473454666962555">اختيار مجموعة علامات التبويب</translation>
 <translation id="7100731960740376323">{TABS_COUNT,plural, =1{مشاركة علامة التبويب}zero{مشاركة علامات التبويب}two{مشاركة علامتَي التبويب}few{مشاركة علامات التبويب}many{مشاركة علامات التبويب}other{مشاركة علامات التبويب}}</translation>
@@ -192,6 +194,7 @@
 <translation id="8029301326595421733">يتم عرض المعلومات حول هذا المتجر بطول الشاشة.</translation>
 <translation id="8053770993984522566">قائمة مجموعات علامات التبويب، مفتوحة في نصف الشاشة</translation>
 <translation id="805809603610022708">{TABS_COUNT,plural, =1{إضافة علامة التبويب إلى المجموعة}zero{إضافة علامات التبويب إلى المجموعة}two{إضافة علامتَي التبويب إلى المجموعة}few{إضافة علامات التبويب إلى المجموعة}many{إضافة علامات التبويب إلى المجموعة}other{إضافة علامات التبويب إلى المجموعة}}</translation>
+<translation id="8138067144082174140">بعد 14 يومًا من عدم النشاط</translation>
 <translation id="8153873338998959464">{NUMBER_OF_TABS,plural, =1{توسيع مجموعة علامات التبويب "<ph name="TITLE_OF_GROUP" />" التي تظهر بلون <ph name="COLOR_NAME" /> وتحتوي على علامة تبويب واحدة (<ph name="TABS_COUNT_ONE" />)}zero{توسيع مجموعة علامات التبويب "<ph name="TITLE_OF_GROUP" />" التي تظهر بلون <ph name="COLOR_NAME" /> وتحتوي على <ph name="TABS_COUNT_MANY" /> علامة تبويب}two{توسيع مجموعة علامات التبويب "<ph name="TITLE_OF_GROUP" />" التي تظهر بلون <ph name="COLOR_NAME" /> وتحتوي على علامتَي تبويب (<ph name="TABS_COUNT_MANY" />)}few{توسيع مجموعة علامات التبويب "<ph name="TITLE_OF_GROUP" />" التي تظهر بلون <ph name="COLOR_NAME" /> وتحتوي على <ph name="TABS_COUNT_MANY" /> علامات تبويب}many{توسيع مجموعة علامات التبويب "<ph name="TITLE_OF_GROUP" />" التي تظهر بلون <ph name="COLOR_NAME" /> وتحتوي على <ph name="TABS_COUNT_MANY" /> علامة تبويب}other{توسيع مجموعة علامات التبويب "<ph name="TITLE_OF_GROUP" />" التي تظهر بلون <ph name="COLOR_NAME" /> وتحتوي على <ph name="TABS_COUNT_MANY" /> علامة تبويب}}</translation>
 <translation id="8205266828577616993">نقل علامة التبويب إلى الأعلى</translation>
 <translation id="8207688828622594111">{NUM_MINS,plural, =1{تم تعديل المجموعة قبل دقيقة واحدة (<ph name="NUM_MINS_ONE" />)}zero{تم تعديل المجموعة قبل <ph name="NUM_MINS_MANY" /> دقيقة}two{تم تعديل المجموعة قبل دقيقتَين (<ph name="NUM_MINS_MANY" />)}few{تم تعديل المجموعة قبل <ph name="NUM_MINS_MANY" /> دقائق}many{تم تعديل المجموعة قبل <ph name="NUM_MINS_MANY" /> دقيقةً}other{تم تعديل المجموعة قبل <ph name="NUM_MINS_MANY" /> دقيقة}}</translation>
diff --git a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_as.xtb b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_as.xtb
index 1a5674d..55d33b1 100644
--- a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_as.xtb
+++ b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_as.xtb
@@ -26,6 +26,7 @@
 <translation id="1738346536511908887">{TAB_GROUPS_COUNT,plural, =1{<ph name="TAB_GROUPS_COUNT_ONE" /> টা টেবৰ গোট আৰু উপলব্ধ নহয়}one{<ph name="TAB_GROUPS_COUNT_MANY" /> টা টেবৰ গোট আৰু উপলব্ধ নহয়}other{<ph name="TAB_GROUPS_COUNT_MANY" /> টা টেবৰ গোট আৰু উপলব্ধ নহয়}}</translation>
 <translation id="176485176228926164"><ph name="BEGIN_LINK1" />অধিক জানক<ph name="END_LINK1" /></translation>
 <translation id="1828808367831369073"><ph name="TABS_COUNT_ONE" />টা টেব এক গোট কৰা হৈছে</translation>
+<translation id="1844381456014878100">৭ দিন নিষ্ক্ৰিয় হৈ থকাৰ পাছত</translation>
 <translation id="1869137256605757565">{TABS_COUNT,plural, =1{<ph name="TABS_COUNT_ONE" />টা টেব}one{<ph name="TABS_COUNT_MANY" />টা টেব}other{<ph name="TABS_COUNT_MANY" />টা টেব}}</translation>
 <translation id="1909810060368225529">জাননী: এই শ্বেয়াৰ কৰা টেবৰ গোটৰ সালসলনিসমূহ সকলোৰে বাবে দৃশ্যমান। আপোনাৰ শ্বেয়াৰ কৰা টেবৰ গোটত যিকোনো লোকে সালসলনি কৰিব পাৰে</translation>
 <translation id="1925363809167540686">{TAB_COUNT,plural, =1{টেব পিন কৰক}one{টেবসমূহ পিন কৰক}other{টেবসমূহ পিন কৰক}}</translation>
@@ -154,6 +155,7 @@
 <translation id="6784585810034073638">{TAB_COUNT,plural, =1{<ph name="TITLE_OF_GROUP" /> নামৰ গোটটোত <ph name="TAB_COUNT1" /> টা টেবৰ জৰিয়তে যোগ দিয়ক}one{<ph name="TITLE_OF_GROUP" /> নামৰ গোটটোত <ph name="TAB_COUNT1" /> টা টেবৰ জৰিয়তে যোগ দিয়ক}other{<ph name="TITLE_OF_GROUP" /> নামৰ গোটটোত <ph name="TAB_COUNT1" /> টা টেবৰ জৰিয়তে যোগ দিয়ক}}</translation>
 <translation id="6818926723028410516">বস্তু বাছনি কৰক</translation>
 <translation id="6856809498882026482">একাধিক বাছনি কৰিব পৰা ম’ডটো লুকুৱাওক</translation>
+<translation id="6957436309971790274">২১ দিন নিষ্ক্ৰিয় হৈ থকাৰ পাছত</translation>
 <translation id="7064815189603108598">{NUMBER_OF_TABS,plural, =1{শ্বেয়াৰ কৰা <ph name="TITLE_OF_GROUP" /> টেবৰ গোটটো <ph name="TABS_COUNT_ONE" /> টা টেব, <ph name="COLOR_NAME" /> ৰঙলৈ বিস্তাৰ কৰক।}one{শ্বেয়াৰ কৰা <ph name="TITLE_OF_GROUP" /> টেবৰ গোটটো <ph name="TABS_COUNT_MANY" /> টা টেব, <ph name="COLOR_NAME" /> ৰঙলৈ বিস্তাৰ কৰক।}other{শ্বেয়াৰ কৰা <ph name="TITLE_OF_GROUP" /> টেবৰ গোটটো <ph name="TABS_COUNT_MANY" /> টা টেব, <ph name="COLOR_NAME" /> ৰঙলৈ বিস্তাৰ কৰক।}}</translation>
 <translation id="709473454666962555">টেবৰ গোট বাছনি কৰক</translation>
 <translation id="7100731960740376323">{TABS_COUNT,plural, =1{টেব শ্বেয়াৰ কৰক}one{টেব শ্বেয়াৰ কৰক}other{টেব শ্বেয়াৰ কৰক}}</translation>
@@ -192,6 +194,7 @@
 <translation id="8029301326595421733">এই দোকানৰ বিষয়ে তথ্য পূৰ্ণ স্ক্ৰীনত দেখুওৱা হৈছে</translation>
 <translation id="8053770993984522566">টেবৰ গোটৰ সূচী, অৰ্ধ উচ্চতাত খোলা হৈছে</translation>
 <translation id="805809603610022708">{TABS_COUNT,plural, =1{গোটত টেব যোগ দিয়ক}one{গোটত টেবসমূহ যোগ দিয়ক}other{গোটত টেবসমূহ যোগ দিয়ক}}</translation>
+<translation id="8138067144082174140">১৪ দিন নিষ্ক্ৰিয় হৈ থকাৰ পাছত</translation>
 <translation id="8153873338998959464">{NUMBER_OF_TABS,plural, =1{<ph name="TITLE_OF_GROUP" /> টেবৰ গোটটো <ph name="TABS_COUNT_ONE" /> টা টেব, <ph name="COLOR_NAME" /> ৰঙৰ সৈতে বিস্তাৰ কৰক।}one{<ph name="TITLE_OF_GROUP" /> টেবৰ গোটটো <ph name="TABS_COUNT_MANY" /> টা টেব, <ph name="COLOR_NAME" /> ৰঙৰ সৈতে বিস্তাৰ কৰক।}other{<ph name="TITLE_OF_GROUP" /> টেবৰ গোটটো <ph name="TABS_COUNT_MANY" /> টা টেব, <ph name="COLOR_NAME" /> ৰঙৰ সৈতে বিস্তাৰ কৰক।}}</translation>
 <translation id="8205266828577616993">টেবটো ওপৰলৈ নিয়ক</translation>
 <translation id="8207688828622594111">{NUM_MINS,plural, =1{<ph name="NUM_MINS_ONE" /> মিনিট পূৰ্বে আপডে’ট কৰা হৈছে}one{<ph name="NUM_MINS_MANY" /> মিনিট পূৰ্বে আপডে’ট কৰা হৈছে}other{<ph name="NUM_MINS_MANY" /> মিনিট পূৰ্বে আপডে’ট কৰা হৈছে}}</translation>
diff --git a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_fa.xtb b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_fa.xtb
index 69c1fd0..80f50e2 100644
--- a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_fa.xtb
+++ b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_fa.xtb
@@ -26,6 +26,7 @@
 <translation id="1738346536511908887">{TAB_GROUPS_COUNT,plural, =1{‫<ph name="TAB_GROUPS_COUNT_ONE" /> گروه زبانه دیگر دردسترس نیست}one{‫<ph name="TAB_GROUPS_COUNT_MANY" /> گروه زبانه دیگر دردسترس نیست}other{‫<ph name="TAB_GROUPS_COUNT_MANY" /> گروه زبانه دیگر دردسترس نیست}}</translation>
 <translation id="176485176228926164"><ph name="BEGIN_LINK1" />بیشتر بدانید<ph name="END_LINK1" /></translation>
 <translation id="1828808367831369073">‫<ph name="TABS_COUNT_ONE" /> زبانه گروه‌بندی شد</translation>
+<translation id="1844381456014878100">پس‌از ۷ روز غیرفعال بودن</translation>
 <translation id="1869137256605757565">{TABS_COUNT,plural, =1{<ph name="TABS_COUNT_ONE" /> زبانه}one{<ph name="TABS_COUNT_MANY" /> زبانه}other{<ph name="TABS_COUNT_MANY" /> زبانه}}</translation>
 <translation id="1909810060368225529">توجه: تغییرات این «گروه زبانه» هم‌رسانی‌شده برای همه نمایان است. همه می‌توانند در گروه‌های زبانه هم‌رسانی‌شده شما تغییراتی ایجاد کنند</translation>
 <translation id="1925363809167540686">{TAB_COUNT,plural, =1{سنجاق کردن زبانه}one{سنجاق کردن زبانه}other{سنجاق کردن زبانه‌ها}}</translation>
@@ -154,6 +155,7 @@
 <translation id="6784585810034073638">{TAB_COUNT,plural, =1{افزودن به گروهی به نام <ph name="TITLE_OF_GROUP" /> با <ph name="TAB_COUNT1" /> زبانه}one{افزودن به گروهی به نام <ph name="TITLE_OF_GROUP" /> با <ph name="TAB_COUNT1" /> زبانه}other{افزودن به گروهی به نام <ph name="TITLE_OF_GROUP" /> با <ph name="TAB_COUNT1" /> زبانه}}</translation>
 <translation id="6818926723028410516">انتخاب موارد</translation>
 <translation id="6856809498882026482">پنهان کردن حالت چندانتخابی</translation>
+<translation id="6957436309971790274">پس‌از ۲۱ روز غیرفعال بودن</translation>
 <translation id="7064815189603108598">{NUMBER_OF_TABS,plural, =1{ازهم باز کردن گروه زبانه <ph name="TITLE_OF_GROUP" /> هم‌رسانی‌شده با <ph name="TABS_COUNT_ONE" /> زبانه، به‌رنگ <ph name="COLOR_NAME" />.}one{ازهم باز کردن گروه زبانه <ph name="TITLE_OF_GROUP" /> هم‌رسانی‌شده با <ph name="TABS_COUNT_MANY" /> زبانه، به‌رنگ <ph name="COLOR_NAME" />.}other{ازهم باز کردن گروه زبانه <ph name="TITLE_OF_GROUP" /> هم‌رسانی‌شده با <ph name="TABS_COUNT_MANY" /> زبانه، به‌رنگ <ph name="COLOR_NAME" />.}}</translation>
 <translation id="709473454666962555">گروه زبانه‌ای انتخاب کنید</translation>
 <translation id="7100731960740376323">{TABS_COUNT,plural, =1{هم‌رسانی برگه}one{هم‌رسانی برگه}other{هم‌رسانی برگه‌ها}}</translation>
@@ -192,6 +194,7 @@
 <translation id="8029301326595421733">اطلاعات مربوط به این فروشگاه به‌صورت کامل باز شده است</translation>
 <translation id="8053770993984522566">فهرست گروه زبانه، به‌صورت نیمه باز شده است</translation>
 <translation id="805809603610022708">{TABS_COUNT,plural, =1{افزودن زبانه به گروه}one{افزودن زبانه به گروه}other{افزودن زبانه‌ها به گروه}}</translation>
+<translation id="8138067144082174140">پس‌از ۱۴ روز غیرفعال بودن</translation>
 <translation id="8153873338998959464">{NUMBER_OF_TABS,plural, =1{ازهم باز کردن گروه زبانه <ph name="TITLE_OF_GROUP" /> با <ph name="TABS_COUNT_ONE" /> زبانه، رنگ <ph name="COLOR_NAME" />.}one{ازهم باز کردن گروه زبانه <ph name="TITLE_OF_GROUP" /> با <ph name="TABS_COUNT_MANY" /> زبانه، رنگ <ph name="COLOR_NAME" />.}other{ازهم باز کردن گروه زبانه <ph name="TITLE_OF_GROUP" /> با <ph name="TABS_COUNT_MANY" /> زبانه، رنگ <ph name="COLOR_NAME" />.}}</translation>
 <translation id="8205266828577616993">انتقال برگه به بالا</translation>
 <translation id="8207688828622594111">{NUM_MINS,plural, =1{‫<ph name="NUM_MINS_ONE" /> دقیقه پیش به‌روز شده است}one{‫<ph name="NUM_MINS_MANY" /> دقیقه پیش به‌روز شده است}other{‫<ph name="NUM_MINS_MANY" /> دقیقه پیش به‌روز شده است}}</translation>
diff --git a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_ka.xtb b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_ka.xtb
index 6576e65..dc65f787 100644
--- a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_ka.xtb
+++ b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_ka.xtb
@@ -26,6 +26,7 @@
 <translation id="1738346536511908887">{TAB_GROUPS_COUNT,plural, =1{ჩანართების <ph name="TAB_GROUPS_COUNT_ONE" /> ჯგუფი აღარ არის ხელმისაწვდომი}other{ჩანართების <ph name="TAB_GROUPS_COUNT_MANY" /> ჯგუფი აღარ არის ხელმისაწვდომი}}</translation>
 <translation id="176485176228926164"><ph name="BEGIN_LINK1" />შეიტყვეთ მეტი<ph name="END_LINK1" /></translation>
 <translation id="1828808367831369073"><ph name="TABS_COUNT_ONE" /> ჩანართი დაჯგუფდა</translation>
+<translation id="1844381456014878100">7-დღიანი უმოქმედობის შემდეგ</translation>
 <translation id="1869137256605757565">{TABS_COUNT,plural, =1{<ph name="TABS_COUNT_ONE" /> ჩანართი}other{<ph name="TABS_COUNT_MANY" /> ჩანართი}}</translation>
 <translation id="1909810060368225529">შეტყობინება: ამ საზიარო ჩანართების ჯგუფში შეტანილი ცვლილებები ხილულია ყველასთვის. ყველას შეუძლია ცვლილებების შეტანა თქვენს საზიარო ჩანართების ჯგუფში</translation>
 <translation id="1925363809167540686">{TAB_COUNT,plural, =1{ჩანართის ჩამაგრება}other{ჩანართების ჩამაგრება}}</translation>
@@ -154,6 +155,7 @@
 <translation id="6784585810034073638">{TAB_COUNT,plural, =1{<ph name="TAB_COUNT1" />-ჩანართიან ჯგუფში „<ph name="TITLE_OF_GROUP" />“ დამატება}other{<ph name="TAB_COUNT1" />-ჩანართიან ჯგუფში „<ph name="TITLE_OF_GROUP" />“ დამატება}}</translation>
 <translation id="6818926723028410516">აირჩიეთ ერთეულები</translation>
 <translation id="6856809498882026482">რამდენიმეარჩევანიანი რეჟიმის დამალვა</translation>
+<translation id="6957436309971790274">21-დღიანი უმოქმედობის შემდეგ</translation>
 <translation id="7064815189603108598">{NUMBER_OF_TABS,plural, =1{გააფართოვეთ ჩანართების <ph name="TABS_COUNT_ONE" />-ჩანართიანი <ph name="COLOR_NAME" /> ფერის გაზიარებული ჯგუფი „<ph name="TITLE_OF_GROUP" />“.}other{გააფართოვეთ ჩანართების <ph name="TABS_COUNT_MANY" />-ჩანართიანი <ph name="COLOR_NAME" /> ფერის გაზიარებული ჯგუფი „<ph name="TITLE_OF_GROUP" />“.}}</translation>
 <translation id="709473454666962555">აირჩიეთ ჩანართების ჯგუფი</translation>
 <translation id="7100731960740376323">{TABS_COUNT,plural, =1{ჩანართის გაზიარება}other{ჩანართების გაზიარება}}</translation>
@@ -192,6 +194,7 @@
 <translation id="8029301326595421733">ამ მაღაზიის შესახებ ინფორმაცია გახსნილია სრულ სიმაღლეზე</translation>
 <translation id="8053770993984522566">ჩანართების ჯგუფების სია, გახსნილია ნახევარ სიმაღლეზე</translation>
 <translation id="805809603610022708">{TABS_COUNT,plural, =1{ჩანართის დამატება ჯგუფში}other{ჩანართის დამატება ჯგუფში}}</translation>
+<translation id="8138067144082174140">14-დღიანი უმოქმედობის შემდეგ</translation>
 <translation id="8153873338998959464">{NUMBER_OF_TABS,plural, =1{გააფართოვეთ ჩანართების <ph name="TABS_COUNT_ONE" />-ჩანართიანი <ph name="COLOR_NAME" /> ფერის ჯგუფი „<ph name="TITLE_OF_GROUP" />“.}other{გააფართოვეთ ჩანართების <ph name="TABS_COUNT_MANY" />-ჩანართიანი <ph name="COLOR_NAME" /> ფერის ჯგუფი „<ph name="TITLE_OF_GROUP" />“.}}</translation>
 <translation id="8205266828577616993">ჩანართის ზემოთ გადატანა</translation>
 <translation id="8207688828622594111">{NUM_MINS,plural, =1{განახლდა <ph name="NUM_MINS_ONE" /> წუთის წინ}other{განახლდა <ph name="NUM_MINS_MANY" /> წუთის წინ}}</translation>
diff --git a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_mn.xtb b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_mn.xtb
index 9cce085..cfeb067 100644
--- a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_mn.xtb
+++ b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_mn.xtb
@@ -26,6 +26,7 @@
 <translation id="1738346536511908887">{TAB_GROUPS_COUNT,plural, =1{<ph name="TAB_GROUPS_COUNT_ONE" /> табын бүлэг цаашид байхгүй болсон}other{<ph name="TAB_GROUPS_COUNT_MANY" /> табын бүлэг цаашид байхгүй болсон}}</translation>
 <translation id="176485176228926164"><ph name="BEGIN_LINK1" />Нэмэлт мэдээлэл авах<ph name="END_LINK1" /></translation>
 <translation id="1828808367831369073"><ph name="TABS_COUNT_ONE" /> табыг бүлэглэсэн</translation>
+<translation id="1844381456014878100">7 хоног идэвхгүй байсны дараа</translation>
 <translation id="1869137256605757565">{TABS_COUNT,plural, =1{<ph name="TABS_COUNT_ONE" /> таб}other{<ph name="TABS_COUNT_MANY" /> таб}}</translation>
 <translation id="1909810060368225529">Мэдэгдэл: Энэ хуваалцсан табын бүлгийн өөрчлөлт хүн бүрд харагдана. Хүн бүр таны хуваалцсан табын бүлэгт өөрчлөлт хийх боломжтой</translation>
 <translation id="1925363809167540686">{TAB_COUNT,plural, =1{Табыг бэхлэх}other{Табуудыг бэхлэх}}</translation>
@@ -154,6 +155,7 @@
 <translation id="6784585810034073638">{TAB_COUNT,plural, =1{<ph name="TAB_COUNT1" /> табтай <ph name="TITLE_OF_GROUP" /> нэртэй бүлэгт нэмнэ үү}other{<ph name="TAB_COUNT1" /> табтай <ph name="TITLE_OF_GROUP" /> нэртэй бүлэгт нэмнэ үү}}</translation>
 <translation id="6818926723028410516">Зүйл сонгох</translation>
 <translation id="6856809498882026482">Олон сонголттой горимыг нуух</translation>
+<translation id="6957436309971790274">21 хоног идэвхгүй байсны дараа</translation>
 <translation id="7064815189603108598">{NUMBER_OF_TABS,plural, =1{<ph name="TABS_COUNT_ONE" /> табтай, <ph name="COLOR_NAME" /> өнгөтэй хуваалцсан <ph name="TITLE_OF_GROUP" /> нэртэй табын бүлгийг дэлгэх.}other{<ph name="TABS_COUNT_MANY" /> табтай, <ph name="COLOR_NAME" /> өнгөтэй хуваалцсан <ph name="TITLE_OF_GROUP" /> нэртэй табын бүлгийг дэлгэх.}}</translation>
 <translation id="709473454666962555">Табын бүлэг сонгоно уу</translation>
 <translation id="7100731960740376323">{TABS_COUNT,plural, =1{Табыг хуваалцах}other{Табуудыг хуваалцах}}</translation>
@@ -192,6 +194,7 @@
 <translation id="8029301326595421733">Энэ дэлгүүрийн талаарх мэдээллийг бүтэн өндөртэйгээр нээсэн</translation>
 <translation id="8053770993984522566">Табын бүлгийн жагсаалт. Тал өндрөөр нээсэн</translation>
 <translation id="805809603610022708">{TABS_COUNT,plural, =1{Табыг бүлэгт нэмэх}other{Табуудыг бүлэгт нэмэх}}</translation>
+<translation id="8138067144082174140">14 хоног идэвхгүй байсны дараа</translation>
 <translation id="8153873338998959464">{NUMBER_OF_TABS,plural, =1{<ph name="TABS_COUNT_ONE" /> таб, <ph name="COLOR_NAME" /> өнгөтэй <ph name="TITLE_OF_GROUP" /> табын бүлгийг дэлгэнэ үү.}other{<ph name="TABS_COUNT_MANY" /> таб, <ph name="COLOR_NAME" /> өнгөтэй <ph name="TITLE_OF_GROUP" /> табын бүлгийг дэлгэнэ үү.}}</translation>
 <translation id="8205266828577616993">Табыг дээш зөөх</translation>
 <translation id="8207688828622594111">{NUM_MINS,plural, =1{<ph name="NUM_MINS_ONE" /> минутын өмнө шинэчилсэн}other{<ph name="NUM_MINS_MANY" /> минутын өмнө шинэчилсэн}}</translation>
diff --git a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_vi.xtb b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_vi.xtb
index 9836f60..0266237 100644
--- a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_vi.xtb
+++ b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_vi.xtb
@@ -26,6 +26,7 @@
 <translation id="1738346536511908887">{TAB_GROUPS_COUNT,plural, =1{<ph name="TAB_GROUPS_COUNT_ONE" /> nhóm thẻ không còn hoạt động.}other{<ph name="TAB_GROUPS_COUNT_MANY" /> nhóm thẻ không còn hoạt động}}</translation>
 <translation id="176485176228926164"><ph name="BEGIN_LINK1" />Tìm hiểu thêm<ph name="END_LINK1" /></translation>
 <translation id="1828808367831369073">Đã nhóm <ph name="TABS_COUNT_ONE" /> thẻ</translation>
+<translation id="1844381456014878100">Sau 7 ngày không hoạt động</translation>
 <translation id="1869137256605757565">{TABS_COUNT,plural, =1{<ph name="TABS_COUNT_ONE" /> tab}other{<ph name="TABS_COUNT_MANY" /> tab}}</translation>
 <translation id="1909810060368225529">Thông báo: Mọi người đều có thể thấy các thay đổi đối với Nhóm thẻ được chia sẻ này. Mọi người đều có thể thay đổi nhóm thẻ bạn đã chia sẻ</translation>
 <translation id="1925363809167540686">{TAB_COUNT,plural, =1{Ghim thẻ}other{Ghim các thẻ}}</translation>
@@ -154,6 +155,7 @@
 <translation id="6784585810034073638">{TAB_COUNT,plural, =1{Thêm vào nhóm <ph name="TITLE_OF_GROUP" /> có <ph name="TAB_COUNT1" /> thẻ}other{Thêm vào nhóm <ph name="TITLE_OF_GROUP" /> có <ph name="TAB_COUNT1" /> thẻ}}</translation>
 <translation id="6818926723028410516">Chọn mục</translation>
 <translation id="6856809498882026482">Ẩn chế độ chọn nhiều thẻ</translation>
+<translation id="6957436309971790274">Sau 21 ngày không hoạt động</translation>
 <translation id="7064815189603108598">{NUMBER_OF_TABS,plural, =1{Mở rộng nhóm thẻ <ph name="TITLE_OF_GROUP" /> được chia sẻ có <ph name="TABS_COUNT_ONE" /> thẻ, màu <ph name="COLOR_NAME" />.}other{Mở rộng nhóm thẻ <ph name="TITLE_OF_GROUP" /> được chia sẻ có <ph name="TABS_COUNT_MANY" /> thẻ, màu <ph name="COLOR_NAME" />.}}</translation>
 <translation id="709473454666962555">Chọn nhóm thẻ</translation>
 <translation id="7100731960740376323">{TABS_COUNT,plural, =1{Chia sẻ thẻ}other{Chia sẻ các thẻ}}</translation>
@@ -192,6 +194,7 @@
 <translation id="8029301326595421733">Thông tin về cửa hàng này đã mở trên toàn màn hình</translation>
 <translation id="8053770993984522566">Danh sách nhóm thẻ, mở trên một nửa màn hình</translation>
 <translation id="805809603610022708">{TABS_COUNT,plural, =1{Thêm thẻ vào nhóm}other{Thêm các thẻ vào nhóm}}</translation>
+<translation id="8138067144082174140">Sau 14 ngày không hoạt động</translation>
 <translation id="8153873338998959464">{NUMBER_OF_TABS,plural, =1{Mở rộng nhóm thẻ <ph name="TITLE_OF_GROUP" /> có <ph name="TABS_COUNT_ONE" /> thẻ, màu <ph name="COLOR_NAME" />.}other{Mở rộng nhóm thẻ <ph name="TITLE_OF_GROUP" /> có <ph name="TABS_COUNT_MANY" /> thẻ, màu <ph name="COLOR_NAME" />.}}</translation>
 <translation id="8205266828577616993">Chuyển thẻ lên trên</translation>
 <translation id="8207688828622594111">{NUM_MINS,plural, =1{Cập nhật <ph name="NUM_MINS_ONE" /> phút trước}other{Cập nhật <ph name="NUM_MINS_MANY" /> phút trước}}</translation>
diff --git a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_zh-HK.xtb b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_zh-HK.xtb
index 60e2427..ca80c1e 100644
--- a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_zh-HK.xtb
+++ b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_zh-HK.xtb
@@ -26,6 +26,7 @@
 <translation id="1738346536511908887">{TAB_GROUPS_COUNT,plural, =1{<ph name="TAB_GROUPS_COUNT_ONE" /> 個分頁群組已無法使用}other{<ph name="TAB_GROUPS_COUNT_MANY" /> 個分頁群組已無法使用}}</translation>
 <translation id="176485176228926164"><ph name="BEGIN_LINK1" />瞭解詳情<ph name="END_LINK1" /></translation>
 <translation id="1828808367831369073">已將 <ph name="TABS_COUNT_ONE" /> 個分頁分組</translation>
+<translation id="1844381456014878100">閒置 7 天後</translation>
 <translation id="1869137256605757565">{TABS_COUNT,plural, =1{<ph name="TABS_COUNT_ONE" /> 個分頁}other{<ph name="TABS_COUNT_MANY" /> 個分頁}}</translation>
 <translation id="1909810060368225529">注意:所有人都能看到此共用分頁群組的變更,所有人都可以變更你的共用分頁群組</translation>
 <translation id="1925363809167540686">{TAB_COUNT,plural, =1{固定單一分頁}other{固定多個分頁}}</translation>
@@ -154,6 +155,7 @@
 <translation id="6784585810034073638">{TAB_COUNT,plural, =1{加去有 <ph name="TAB_COUNT1" /> 個分頁嘅「<ph name="TITLE_OF_GROUP" />」群組}other{加去有 <ph name="TAB_COUNT1" /> 個分頁嘅「<ph name="TITLE_OF_GROUP" />」群組}}</translation>
 <translation id="6818926723028410516">選取項目</translation>
 <translation id="6856809498882026482">隱藏多重選取模式</translation>
+<translation id="6957436309971790274">閒置 21 天後</translation>
 <translation id="7064815189603108598">{NUMBER_OF_TABS,plural, =1{展開有 <ph name="TABS_COUNT_ONE" /> 個分頁嘅共享分頁群組「<ph name="TITLE_OF_GROUP" />」,顏色係<ph name="COLOR_NAME" />。}other{展開有 <ph name="TABS_COUNT_MANY" /> 個分頁嘅共享分頁群組「<ph name="TITLE_OF_GROUP" />」,顏色係<ph name="COLOR_NAME" />。}}</translation>
 <translation id="709473454666962555">選取分頁群組</translation>
 <translation id="7100731960740376323">{TABS_COUNT,plural, =1{分享分頁}other{分享分頁}}</translation>
@@ -192,6 +194,7 @@
 <translation id="8029301326595421733">關於呢間商店嘅資料宜家顯示喺成個畫面</translation>
 <translation id="8053770993984522566">分頁群組清單,目前顯示於畫面下半部</translation>
 <translation id="805809603610022708">{TABS_COUNT,plural, =1{將分頁加入群組}other{將分頁加入群組}}</translation>
+<translation id="8138067144082174140">閒置 14 天後</translation>
 <translation id="8153873338998959464">{NUMBER_OF_TABS,plural, =1{展開有 <ph name="TABS_COUNT_ONE" /> 個分頁嘅「<ph name="TITLE_OF_GROUP" />」分頁群組,顏色係<ph name="COLOR_NAME" />。}other{展開有 <ph name="TABS_COUNT_MANY" /> 個分頁嘅「<ph name="TITLE_OF_GROUP" />」分頁群組,顏色係<ph name="COLOR_NAME" />。}}</translation>
 <translation id="8205266828577616993">將分頁上移</translation>
 <translation id="8207688828622594111">{NUM_MINS,plural, =1{<ph name="NUM_MINS_ONE" /> 分鐘前更新}other{<ph name="NUM_MINS_MANY" /> 分鐘前更新}}</translation>
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 67adbad..b2151944 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -39,17 +39,6 @@
     <uses-permission-sdk-23 android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
-    <!--
-      Enable runtime permissions as uses-permission in tip of tree builds
-      only for ease of development on Android L and earlier. For consumer
-      channels use "runtime permission" uses-permission-sdk-23 which provides
-      permission on Android M and later without a prompt.
-    -->
-    {% if channel in ['default'] %}
-        <!--  Needed for allowing downloaded APKs to be installed. -->
-        <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
-    {% endif %}
-
     <uses-permission-sdk-23 android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION" />
     <uses-permission-sdk-23 android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/>
     <uses-permission-sdk-23 android:name="android.permission.BLUETOOTH_CONNECT"/>
@@ -75,6 +64,8 @@
 
     <uses-permission-sdk-23 android:name="android.permission.READ_CONTACTS"/>
     <uses-permission-sdk-23 android:name="android.permission.REORDER_TASKS"/>
+
+    <!--  Needed for allowing downloaded APKs to be installed. -->
     <uses-permission-sdk-23 android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
 
     <uses-permission android:name="android.permission.CAMERA" />
diff --git a/chrome/android/java/res/drawable/add_link.xml b/chrome/android/java/res/drawable/add_link.xml
new file mode 100644
index 0000000..65d91ecb
--- /dev/null
+++ b/chrome/android/java/res/drawable/add_link.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2025 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960">
+  <path
+      android:pathData="M680,800v-120L560,680v-80h120v-120h80v120h120v80L760,680v120h-80ZM440,680L280,680q-83,0 -141.5,-58.5T80,480q0,-83 58.5,-141.5T280,280h160v80L280,360q-50,0 -85,35t-35,85q0,50 35,85t85,35h160v80ZM320,520v-80h320v80L320,520ZM880,480h-80q0,-50 -35,-85t-85,-35L520,360v-80h160q83,0 141.5,58.5T880,480Z"
+      android:fillColor="@macro/default_icon_color"/>
+</vector>
diff --git a/chrome/android/java/res/drawable/checklist.xml b/chrome/android/java/res/drawable/checklist.xml
new file mode 100644
index 0000000..18a3d10
--- /dev/null
+++ b/chrome/android/java/res/drawable/checklist.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2025 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960">
+  <path
+      android:pathData="M222,760 L80,618l56,-56 85,85 170,-170 56,57 -225,226ZM222,440L80,298l56,-56 85,85 170,-170 56,57 -225,226ZM520,680v-80h360v80L520,680ZM520,360v-80h360v80L520,360Z"
+      android:fillColor="@macro/default_icon_color"/>
+</vector>
diff --git a/chrome/android/java/res/drawable/receipt_long.xml b/chrome/android/java/res/drawable/receipt_long.xml
new file mode 100644
index 0000000..16629cd
--- /dev/null
+++ b/chrome/android/java/res/drawable/receipt_long.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2025 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960">
+  <path
+      android:pathData="M240,880q-50,0 -85,-35t-35,-85v-120h120v-560l60,60 60,-60 60,60 60,-60 60,60 60,-60 60,60 60,-60 60,60 60,-60v680q0,50 -35,85t-85,35L240,880ZM720,800q17,0 28.5,-11.5T760,760v-560L320,200v440h360v120q0,17 11.5,28.5T720,800ZM360,360v-80h240v80L360,360ZM360,480v-80h240v80L360,480ZM680,360q-17,0 -28.5,-11.5T640,320q0,-17 11.5,-28.5T680,280q17,0 28.5,11.5T720,320q0,17 -11.5,28.5T680,360ZM680,480q-17,0 -28.5,-11.5T640,440q0,-17 11.5,-28.5T680,400q17,0 28.5,11.5T720,440q0,17 -11.5,28.5T680,480ZM240,800h360v-80L200,720v40q0,17 11.5,28.5T240,800ZM200,800v-80,80Z"
+      android:fillColor="@macro/default_icon_color"/>
+</vector>
diff --git a/chrome/android/java/res/layout/fake_search_box_layout.xml b/chrome/android/java/res/layout/fake_search_box_layout.xml
index b5745b8e..2134306 100644
--- a/chrome/android/java/res/layout/fake_search_box_layout.xml
+++ b/chrome/android/java/res/layout/fake_search_box_layout.xml
@@ -4,12 +4,11 @@
 Use of this source code is governed by a BSD-style license that can be
 found in the LICENSE file.
 -->
-
 <view
-    class="org.chromium.chrome.browser.ntp.search.SearchBoxContainerView"
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
+    class="org.chromium.chrome.browser.ntp.search.SearchBoxContainerView"
     android:id="@+id/search_box"
     android:layout_width="match_parent"
     android:layout_height="@dimen/ntp_search_box_height"
@@ -22,7 +21,6 @@
     android:orientation="horizontal"
     android:paddingStart="@dimen/fake_search_box_start_padding"
     android:paddingEnd="@dimen/fake_search_box_end_padding">
-
     <!-- Fake searchbox is only showing when Google is the DSE. -->
     <org.chromium.ui.widget.ChromeImageView
         android:id="@+id/search_box_engine_icon"
@@ -34,7 +32,7 @@
         android:focusable="false"
         android:importantForAccessibility="no"
         android:visibility="gone"
-        android:src="@drawable/ic_logo_googleg_24dp" />
+        android:src="@drawable/ic_logo_googleg_24dp"/>
 
     <!-- TODO(crbug.com/40600572): Fix and remove lint ignore -->
     <RelativeLayout
@@ -42,19 +40,23 @@
         android:layout_height="match_parent"
         android:layout_weight="1"
         android:gravity="center_vertical">
+
         <EditText
             tools:ignore="Autofill,LabelFor"
             style="@style/TextAppearance.FakeSearchBoxText"
             android:id="@+id/search_box_text"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:textAlignment="viewStart"
             android:background="@null"
             android:ellipsize="end"
             android:focusableInTouchMode="false"
             android:inputType="text"
             android:singleLine="true"
-            android:hint="@string/omnibox_empty_hint" />
+            android:hint="@string/omnibox_empty_hint"/>
+
     </RelativeLayout>
+
     <com.airbnb.lottie.LottieAnimationView
         android:id="@+id/composeplate_button"
         style="@style/LocationBarActionButtonForFakeSearchBox"
@@ -64,15 +66,18 @@
         app:lottie_autoPlay="true"
         app:lottie_loop="true"
         android:visibility="gone"/>
+
     <org.chromium.ui.widget.ChromeImageView
         android:id="@+id/voice_search_button"
         style="@style/LocationBarActionButtonForFakeSearchBox"
         android:contentDescription="@string/accessibility_toolbar_btn_mic"
-        android:src="@drawable/ic_mic_white_24dp" />
+        android:src="@drawable/ic_mic_white_24dp"/>
+
     <org.chromium.ui.widget.ChromeImageView
         android:id="@+id/lens_camera_button"
         style="@style/LocationBarActionButtonForFakeSearchBox"
         android:contentDescription="@string/accessibility_btn_lens_camera"
         android:src="@drawable/lens_camera_icon"
-        android:visibility="gone" />
+        android:visibility="gone"/>
+
 </view>
diff --git a/chrome/android/java/res/layout/url_bar.xml b/chrome/android/java/res/layout/url_bar.xml
index a707fae5..0e66bf9 100644
--- a/chrome/android/java/res/layout/url_bar.xml
+++ b/chrome/android/java/res/layout/url_bar.xml
@@ -17,6 +17,7 @@
     android:imeOptions="actionGo|flagNoExtractUi|flagNoFullscreen"
     android:textAppearance="@style/TextAppearance.SearchBoxText"
     android:hint="@string/omnibox_empty_hint"
+    android:textAlignment="viewStart"
     app:layout_constraintStart_toEndOf="@id/location_bar_status"
     app:layout_constraintEnd_toStartOf="@id/url_action_container"
     app:layout_goneMarginEnd="@dimen/location_bar_end_padding"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 369bd54..ae391ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -173,6 +173,7 @@
 import org.chromium.chrome.browser.navigation_predictor.NavigationPredictorBridge;
 import org.chromium.chrome.browser.new_tab_url.DseNewTabUrlManager;
 import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
+import org.chromium.chrome.browser.notifications.scheduler.TipsNotificationsFeatureType;
 import org.chromium.chrome.browser.notifications.tips.TipsPromoCoordinator;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.ntp.NewTabPageLaunchOrigin;
@@ -201,6 +202,8 @@
 import org.chromium.chrome.browser.read_later.ReadingListBackPressHandler;
 import org.chromium.chrome.browser.recent_tabs.CrossDevicePaneFactory;
 import org.chromium.chrome.browser.reengagement.ReengagementNotificationController;
+import org.chromium.chrome.browser.safe_browsing.metrics.SettingsAccessPoint;
+import org.chromium.chrome.browser.safe_browsing.settings.SafeBrowsingSettingsFragment;
 import org.chromium.chrome.browser.safety_hub.SafetyHubMagicStackBuilder;
 import org.chromium.chrome.browser.search_engines.SearchEngineChoiceNotification;
 import org.chromium.chrome.browser.searchwidget.SearchActivityClientImpl;
@@ -1867,7 +1870,9 @@
                 AsyncTabParamsManagerSingleton.getInstance().hasParamsForTabId(tabId);
         if (url == null
                 && tabIdToBringToFront == Tab.INVALID_TAB_ID
-                && !hasTabWaitingForReparenting) return false;
+                && !hasTabWaitingForReparenting) {
+            return false;
+        }
 
         LoadUrlParams loadUrlParams =
                 IntentHandler.createLoadUrlParamsForIntent(url, intent, mIntentHandlingTimeMs);
@@ -1893,7 +1898,9 @@
                         intent);
         boolean shouldPin = IntentHandler.getPinnedState(intent);
         if (shouldPin && !tab.getIsPinned()) {
-            getTabModelSelector().getModel(tab.isIncognito()).pinTab(tab.getId());
+            getTabModelSelector()
+                    .getModel(tab.isIncognito())
+                    .pinTab(tab.getId(), /* showUngroupDialog= */ false);
         }
         int destTabId = IntentHandler.getDestTabId(intent);
         if (destTabId != Tab.INVALID_TAB_ID) {
@@ -1939,7 +1946,7 @@
 
             Tab tab = processTabIntentAndLoadTab(intent, tabId, url, tabOpenType);
             if (isPinned[i] && !tab.getIsPinned()) {
-                tabModel.pinTab(tab.getId());
+                tabModel.pinTab(tab.getId(), /* showUngroupDialog= */ false);
             }
             tabs.add(tab);
         }
@@ -2521,6 +2528,7 @@
         boolean fromAppWidget =
                 IntentUtils.safeGetBooleanExtra(
                         intent, IntentHandler.EXTRA_INVOKED_FROM_APP_WIDGET, false);
+        @TipsNotificationsFeatureType
         int fromTipsNotifications =
                 IntentUtils.safeGetIntExtra(
                         intent,
@@ -2659,7 +2667,9 @@
                     mTipsPromoCoordinator =
                             new TipsPromoCoordinator(
                                     this, mRootUiCoordinator.getBottomSheetController());
-                    mTipsPromoCoordinator.showBottomSheet(fromTipsNotifications);
+                    mTipsPromoCoordinator.showBottomSheet(
+                            fromTipsNotifications,
+                            () -> getTipsNotificationsFeatureTipAction(fromTipsNotifications));
                 }
                 break;
             case TabOpenType.OPEN_NEW_INCOGNITO_TAB:
@@ -2762,6 +2772,26 @@
         return resultTab;
     }
 
+    // TODO(crbug.com/449743910): Refactor this logic to TipsNotifications module after all the
+    // feature tip launch logic is added, if possible.
+    private void getTipsNotificationsFeatureTipAction(
+            @TipsNotificationsFeatureType int featureType) {
+        switch (featureType) {
+            case TipsNotificationsFeatureType.ENHANCED_SAFE_BROWSING:
+                Intent intent =
+                        SettingsNavigationFactory.createSettingsNavigation()
+                                .createSettingsIntent(
+                                        this,
+                                        SafeBrowsingSettingsFragment.class,
+                                        SafeBrowsingSettingsFragment.createArguments(
+                                                SettingsAccessPoint.TIPS_NOTIFICATIONS_PROMO));
+                startActivity(intent);
+                break;
+            default:
+                assert false : "Invalid feature type: " + featureType;
+        }
+    }
+
     private boolean isProbablyFromChrome(Intent intent, String externalAppId) {
         // To determine if the processed intent is from Chrome, check for any of the following:
         // 1.) The authentication token that will be added to trusted intents.
@@ -3991,7 +4021,7 @@
                             PageTransition.AUTO_TOPLEVEL));
         } else if (id == R.id.pin_tab_menu_id) {
             TabModel tabModel = mTabModelSelector.getCurrentModel();
-            tabModel.pinTab(currentTab.getId());
+            tabModel.pinTab(currentTab.getId(), /* showUngroupDialog= */ true);
         } else if (id == R.id.unpin_tab_menu_id) {
             TabModel tabModel = mTabModelSelector.getCurrentModel();
             tabModel.unpinTab(currentTab.getId());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
index f59142c6..d88141fd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser;
 
+import static org.chromium.build.NullUtil.assertNonNull;
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.ActivityManager.RecentTaskInfo;
@@ -30,6 +33,8 @@
 import org.chromium.base.Log;
 import org.chromium.base.PackageManagerUtils;
 import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browserservices.SessionDataHolder;
 import org.chromium.chrome.browser.browserservices.intents.SessionHolder;
@@ -65,6 +70,7 @@
  * Dispatches incoming intents to the appropriate activity based on the current configuration and
  * Intent fired.
  */
+@NullMarked
 public class LaunchIntentDispatcher {
     /** Extra indicating launch mode used. */
     public static final String EXTRA_LAUNCH_MODE =
@@ -142,11 +148,11 @@
 
     private LaunchIntentDispatcher(Activity activity, Intent intent) {
         mActivity = activity;
-        mIntent = IntentUtils.sanitizeIntent(intent);
+        mIntent = assertNonNull(IntentUtils.sanitizeIntent(intent));
 
         // Needs to be called as early as possible, to accurately capture the
         // time at which the intent was received.
-        if (mIntent != null && BrowserIntentUtils.getStartupRealtimeMillis(mIntent) == -1) {
+        if (BrowserIntentUtils.getStartupRealtimeMillis(mIntent) == -1) {
             BrowserIntentUtils.addStartupTimestampsToIntent(mIntent);
         }
     }
@@ -250,7 +256,7 @@
 
     /** When started with an intent, maybe pre-resolve the domain. */
     private void maybePrefetchDnsInBackground() {
-        if (mIntent != null && Intent.ACTION_VIEW.equals(mIntent.getAction())) {
+        if (Intent.ACTION_VIEW.equals(mIntent.getAction())) {
             String maybeUrl = IntentHandler.getUrlFromIntent(mIntent);
             if (maybeUrl != null) {
                 WarmupManager.getInstance().maybePrefetchDnsForUrlInBackground(mActivity, maybeUrl);
@@ -473,7 +479,7 @@
      * Returns client package name obtained from {@link Activity#getLaunchedFromPackage()}. {@code
      * null} if the underlying OS doesn't support the feature.
      */
-    private String getCallingPackageIdentitySharing() {
+    private @Nullable String getCallingPackageIdentitySharing() {
         return BuildCompat.isAtLeastU() ? mActivity.getLaunchedFromPackage() : null;
     }
 
@@ -536,7 +542,9 @@
             newIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
         }
 
-        if (newIntent.getComponent().getClassName().equals(mActivity.getClass().getName())) {
+        String className = assumeNonNull(newIntent.getComponent()).getClassName();
+        assumeNonNull(className);
+        if (className.equals(mActivity.getClass().getName())) {
             // We're trying to start activity that is already running - just continue.
             return Action.CONTINUE;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabbedModeTabModelOrchestrator.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabbedModeTabModelOrchestrator.java
index b226850f..7caf0f3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabbedModeTabModelOrchestrator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabbedModeTabModelOrchestrator.java
@@ -234,7 +234,8 @@
             createArchivedTabModelInDeferredTask(tabContentManager);
         }
 
-        if (ChromeFeatureList.sTabStorageSqlitePrototype.isEnabled()) {
+        if (ChromeFeatureList.sTabStorageSqlitePrototype.isEnabled()
+                && ChromeFeatureList.sTabCollectionAndroid.isEnabled()) {
             mTabStateStoreIsAuthoritative =
                     ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
                             ChromeFeatureList.TAB_STORAGE_SQLITE_PROTOTYPE,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
index 275f1195..62e41e6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -266,7 +266,8 @@
     private final ObservableSupplier<LayerTitleCache> mLayerTitleCacheSupplier;
     private final BrowserControlsStateProvider mBrowserControlsStateProvider;
     private final Callback<Integer> mStripVisibilityStateObserver;
-    private final ObservableSupplierImpl<Integer> mStripVisibilityStateSupplier;
+    private final ObservableSupplierImpl<@StripVisibilityState Integer>
+            mStripVisibilityStateSupplier;
     private final @Nullable ObservableSupplier<Boolean> mXrSpaceModeObservableSupplier;
     private final TopControlsStacker mTopControlsStacker;
 
@@ -1670,7 +1671,7 @@
     }
 
     @Override
-    public ObservableSupplier<Integer> getStripVisibilityStateSupplier() {
+    public ObservableSupplier<@StripVisibilityState Integer> getStripVisibilityStateSupplier() {
         // TODO(crbug.com/417238089): get() returns a stale value during height transitions.
         return mStripVisibilityStateSupplier;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinator.java
index cc2d62f..6770707 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinator.java
@@ -53,6 +53,7 @@
 import org.chromium.chrome.browser.tabmodel.TabClosureParams;
 import org.chromium.chrome.browser.tabmodel.TabClosureParamsUtils;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabGroupTitleUtils;
 import org.chromium.chrome.browser.tabmodel.TabGroupUtils;
 import org.chromium.chrome.browser.tabmodel.TabGroupUtils.TabGroupCreationCallback;
 import org.chromium.chrome.browser.tabmodel.TabList;
@@ -211,7 +212,7 @@
                         .share(tabs.get(0), /* shareDirectly= */ false, TAB_STRIP_CONTEXT_MENU);
             } else if (menuId == R.id.pin_tab_menu_id) {
                 for (Tab tab : tabs) {
-                    tabModel.pinTab(tab.getId());
+                    tabModel.pinTab(tab.getId(), /* showUngroupDialog= */ tabs.size() == 1);
                 }
             } else if (menuId == R.id.unpin_tab_menu_id) {
                 // Unpinning in reverse to maintain the order of the tabs.
@@ -586,7 +587,10 @@
                     new ListItem(
                             MENU_ITEM,
                             new PropertyModel.Builder(ListMenuItemProperties.ALL_KEYS)
-                                    .with(TITLE, mTabGroupModelFilter.getTabGroupTitle(groupId))
+                                    .with(
+                                            TITLE,
+                                            TabGroupTitleUtils.getDisplayableTitle(
+                                                    mContext, mTabGroupModelFilter, groupId))
                                     .with(ENABLED, true)
                                     .with(CLICK_LISTENER, clickListener)
                                     .with(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/MultiTabReorderStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/MultiTabReorderStrategy.java
index 406cd0b9..62aea4c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/MultiTabReorderStrategy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/MultiTabReorderStrategy.java
@@ -7,9 +7,11 @@
 import static org.chromium.build.NullUtil.assumeNonNull;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.graphics.PointF;
 import android.view.View;
 
+import org.chromium.base.MathUtils;
 import org.chromium.base.Token;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplierImpl;
@@ -19,6 +21,7 @@
 import org.chromium.chrome.browser.compositor.overlays.strip.ScrollDelegate;
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutGroupTitle;
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutTab;
+import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutTabDelegate;
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutUtils;
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutView;
 import org.chromium.chrome.browser.compositor.overlays.strip.StripTabModelActionListener;
@@ -100,55 +103,17 @@
         RecordUserAction.record("MobileToolbarStartMultiTabReorder");
         mPrimaryInteractingStripTab = (StripLayoutTab) interactingView;
         Tab primaryTab = mModel.getTabById(mPrimaryInteractingStripTab.getTabId());
-        if (primaryTab == null) return;
-
+        if (primaryTab == null) {
+            mPrimaryInteractingStripTab = null;
+            return;
+        }
         TabModelUtils.setIndex(mModel, mModel.indexOf(primaryTab));
 
         List<Tab> selectedTabs = getSortedSelectedTabs(stripTabs);
-        for (Tab tab : selectedTabs) {
-            int tabId = tab.getId();
-            mInteractingTabs.add(StripLayoutUtils.findTabById(stripTabs, tabId));
-            mInteractingTabIds.add(tabId);
-        }
-        mFirstTabInBlock = mInteractingTabs.get(0);
-        mLastTabInBlock = mInteractingTabs.get(mInteractingTabs.size() - 1);
 
-        boolean isPrimaryTabInGroup = mTabGroupModelFilter.isTabInTabGroup(primaryTab);
-        boolean notAllTabsInPrimaryGroupAreSelected = false;
-        List<Tab> tabsInGroup = mTabGroupModelFilter.getTabsInGroup(primaryTab.getTabGroupId());
-        for (Tab tab : tabsInGroup) {
-            if (!mInteractingTabIds.contains(tab.getId())) {
-                notAllTabsInPrimaryGroupAreSelected = true;
-                break;
-            }
-        }
+        setupReorderState(stripTabs, selectedTabs);
 
-        if (isPrimaryTabInGroup && notAllTabsInPrimaryGroupAreSelected) {
-            Token destinationGroupId = primaryTab.getTabGroupId();
-            assert destinationGroupId != null;
-            int primaryTabIndexInGroup = tabsInGroup.indexOf(primaryTab);
-            mTabGroupModelFilter.mergeListOfTabsToGroup(
-                    selectedTabs,
-                    primaryTab,
-                    /* indexInGroup= */ primaryTabIndexInGroup,
-                    /* notify= */ MergeNotificationType.DONT_NOTIFY);
-        } else {
-            ungroupInteractingBlock();
-            int primaryTabModelIndex = mModel.indexOf(primaryTab);
-            int primaryTabIndexInSelection = selectedTabs.indexOf(primaryTab);
-
-            int targetGatherIndex = primaryTabModelIndex - primaryTabIndexInSelection;
-            for (int i = 0; i <= selectedTabs.size() - 1; i++) {
-                Tab tab = selectedTabs.get(i);
-                int currentTabModelIndex = mModel.indexOf(tab);
-                if (currentTabModelIndex > targetGatherIndex) {
-                    targetGatherIndex++;
-                } else if (currentTabModelIndex == targetGatherIndex) {
-                    continue;
-                }
-                mModel.moveTab(selectedTabs.get(i).getId(), targetGatherIndex);
-            }
-        }
+        gatherBlock(primaryTab, selectedTabs);
 
         for (StripLayoutView view : mInteractingTabs) {
             view.setIsForegrounded(/* isForegrounded= */ true);
@@ -224,6 +189,142 @@
         }
     }
 
+    @Override
+    public void reorderViewInDirection(
+            StripLayoutTabDelegate tabDelegate,
+            StripLayoutView[] stripViews,
+            StripLayoutGroupTitle[] groupTitles,
+            StripLayoutTab[] stripTabs,
+            StripLayoutView reorderingView,
+            boolean toLeft) {
+        // Cast to the correct view type.
+        assert reorderingView instanceof StripLayoutTab && mModel.getMultiSelectedTabsCount() > 1
+                : "Using incorrect ReorderStrategy for view type.";
+
+        mPrimaryInteractingStripTab = (StripLayoutTab) reorderingView;
+        Tab primaryTab = mModel.getTabById(mPrimaryInteractingStripTab.getTabId());
+        if (primaryTab == null) {
+            mPrimaryInteractingStripTab = null;
+            return;
+        }
+        TabModelUtils.setIndex(mModel, mModel.indexOf(primaryTab));
+
+        List<Tab> selectedTabs = getSortedSelectedTabs(stripTabs);
+
+        setupReorderState(stripTabs, selectedTabs);
+
+        gatherBlock(primaryTab, selectedTabs);
+
+        // Fake a successful reorder in the target direction.
+        float offset = MathUtils.flipSignIf(Float.MAX_VALUE, toLeft);
+        reorderBlockIfThresholdReached(stripViews, groupTitles, stripTabs, offset);
+
+        // Animate the reordering view and ensure it's foregrounded.
+        for (StripLayoutTab tab : mInteractingTabs) {
+            tabDelegate.setIsTabNonDragReordering(tab, /* isNonDragReordering= */ true);
+            tab.setIsForegrounded(/* isForegrounded= */ true);
+            animateViewSliding(tab);
+        }
+        animateViewSliding(
+                reorderingView,
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        for (StripLayoutTab tab : mInteractingTabs) {
+                            tabDelegate.setIsTabNonDragReordering(
+                                    tab, /* isNonDragReordering= */ false);
+                            tab.setIsForegrounded(/* isForegrounded= */ false);
+                        }
+                        clearReorderState();
+                    }
+                });
+    }
+
+    @Override
+    public void stopReorderMode(StripLayoutView[] stripViews, StripLayoutGroupTitle[] groupTitles) {
+        mAnimationHost.finishAnimationsAndPushTabUpdates();
+        ArrayList<Animator> animationList = new ArrayList<>();
+        Runnable onAnimationEnd =
+                () -> {
+                    for (StripLayoutTab tab : mInteractingTabs) {
+                        if (tab != null) {
+                            tab.setIsForegrounded(false);
+                        }
+                    }
+                    clearReorderState();
+                };
+        List<StripLayoutView> interactingViews = new ArrayList<>(mInteractingTabs);
+        handleStopReorderMode(
+                stripViews,
+                groupTitles,
+                interactingViews,
+                mPrimaryInteractingStripTab,
+                animationList,
+                onAnimationEnd);
+    }
+
+    private void setupReorderState(StripLayoutTab[] stripTabs, List<Tab> selectedTabs) {
+        // Ensure state is clean before starting a new reorder.
+        assert mInteractingTabs.isEmpty();
+        assert mInteractingTabIds.isEmpty();
+
+        for (Tab tab : selectedTabs) {
+            int tabId = tab.getId();
+            mInteractingTabs.add(StripLayoutUtils.findTabById(stripTabs, tabId));
+            mInteractingTabIds.add(tabId);
+        }
+
+        mFirstTabInBlock = mInteractingTabs.get(0);
+        mLastTabInBlock = mInteractingTabs.get(mInteractingTabs.size() - 1);
+    }
+
+    private void clearReorderState() {
+        mInteractingTabs.clear();
+        mInteractingTabIds.clear();
+        mPrimaryInteractingStripTab = null;
+        mFirstTabInBlock = null;
+        mLastTabInBlock = null;
+    }
+
+    private void gatherBlock(Tab primaryTab, List<Tab> selectedTabs) {
+        boolean isPrimaryTabInGroup = mTabGroupModelFilter.isTabInTabGroup(primaryTab);
+        boolean notAllTabsInPrimaryGroupAreSelected = false;
+        List<Tab> tabsInGroup = mTabGroupModelFilter.getTabsInGroup(primaryTab.getTabGroupId());
+        for (Tab tab : tabsInGroup) {
+            if (!mInteractingTabIds.contains(tab.getId())) {
+                notAllTabsInPrimaryGroupAreSelected = true;
+                break;
+            }
+        }
+
+        if (isPrimaryTabInGroup && notAllTabsInPrimaryGroupAreSelected) {
+            Token destinationGroupId = primaryTab.getTabGroupId();
+            assert destinationGroupId != null;
+            int primaryTabIndexInGroup = tabsInGroup.indexOf(primaryTab);
+            mTabGroupModelFilter.mergeListOfTabsToGroup(
+                    selectedTabs,
+                    primaryTab,
+                    /* indexInGroup= */ primaryTabIndexInGroup,
+                    /* notify= */ MergeNotificationType.DONT_NOTIFY);
+        } else {
+            ungroupInteractingBlock();
+            int primaryTabModelIndex = mModel.indexOf(primaryTab);
+            int primaryTabIndexInSelection = selectedTabs.indexOf(primaryTab);
+
+            int targetGatherIndex = primaryTabModelIndex - primaryTabIndexInSelection;
+            for (int i = 0; i <= selectedTabs.size() - 1; i++) {
+                Tab tab = selectedTabs.get(i);
+                int currentTabModelIndex = mModel.indexOf(tab);
+                if (currentTabModelIndex > targetGatherIndex) {
+                    targetGatherIndex++;
+                } else if (currentTabModelIndex == targetGatherIndex) {
+                    continue;
+                }
+                mModel.moveTab(selectedTabs.get(i).getId(), targetGatherIndex);
+            }
+        }
+    }
+
     /**
      * Determines if the drag offset has surpassed a reorder threshold and executes the reorder if
      * it has. A reorder can be a simple tab swap, moving past a group, merging into a group, or
@@ -319,31 +420,6 @@
     }
 
     @Override
-    public void stopReorderMode(StripLayoutView[] stripViews, StripLayoutGroupTitle[] groupTitles) {
-        mAnimationHost.finishAnimationsAndPushTabUpdates();
-        ArrayList<Animator> animationList = new ArrayList<>();
-        Runnable onAnimationEnd =
-                () -> {
-                    for (StripLayoutTab tab : mInteractingTabs) {
-                        if (tab != null) {
-                            tab.setIsForegrounded(false);
-                        }
-                    }
-                    mInteractingTabs.clear();
-                    mInteractingTabIds.clear();
-                    mPrimaryInteractingStripTab = null;
-                };
-        List<StripLayoutView> interactingViews = new ArrayList<>(mInteractingTabs);
-        handleStopReorderMode(
-                stripViews,
-                groupTitles,
-                interactingViews,
-                mPrimaryInteractingStripTab,
-                animationList,
-                onAnimationEnd);
-    }
-
-    @Override
     public @Nullable StripLayoutView getInteractingView() {
         return mPrimaryInteractingStripTab;
     }
@@ -474,4 +550,8 @@
                 .getTabUngrouper()
                 .ungroupTabs(tabsToUngroup, /* trailing= */ false, /* allowDialog= */ false, null);
     }
+
+    public void clearReorderStateForTesting() {
+        clearReorderState();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/ReorderDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/ReorderDelegate.java
index ce0e515..dd96417 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/ReorderDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/ReorderDelegate.java
@@ -532,8 +532,14 @@
             StripLayoutView reorderingView,
             boolean toLeft) {
         if (reorderingView instanceof StripLayoutTab) {
-            mTabStrategy.reorderViewInDirection(
-                    tabDelegate, stripViews, groupTitles, stripTabs, reorderingView, toLeft);
+            if (mModel.isTabMultiSelected(((StripLayoutTab) reorderingView).getTabId())
+                    && mModel.getMultiSelectedTabsCount() > 1) {
+                mMultiTabStrategy.reorderViewInDirection(
+                        tabDelegate, stripViews, groupTitles, stripTabs, reorderingView, toLeft);
+            } else {
+                mTabStrategy.reorderViewInDirection(
+                        tabDelegate, stripViews, groupTitles, stripTabs, reorderingView, toLeft);
+            }
         } else if (reorderingView instanceof StripLayoutGroupTitle) {
             mGroupStrategy.reorderViewInDirection(
                     tabDelegate, stripViews, groupTitles, stripTabs, reorderingView, toLeft);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
index 194ddc3..77d952ea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
@@ -816,7 +816,7 @@
         callOnSession(session, params -> params.mAllowParallelRequest = allowed);
     }
 
-    public boolean getAllowParallelRequestForSession(SessionHolder<?> session) {
+    public boolean getAllowParallelRequestForSession(@Nullable SessionHolder<?> session) {
         return callOnSession(session, false, params -> params.mAllowParallelRequest);
     }
 
@@ -824,7 +824,7 @@
         callOnSession(session, params -> params.mAllowResourcePrefetch = allowed);
     }
 
-    public boolean getAllowResourcePrefetchForSession(SessionHolder<?> session) {
+    public boolean getAllowResourcePrefetchForSession(@Nullable SessionHolder<?> session) {
         return callOnSession(session, false, params -> params.mAllowResourcePrefetch);
     }
 
@@ -846,7 +846,7 @@
      * @param origin Origin to verify
      */
     public synchronized boolean isFirstPartyOriginForSession(
-            SessionHolder<?> session, Origin origin) {
+            @Nullable SessionHolder<?> session, Origin origin) {
         return ChromeOriginVerifier.wasPreviouslyVerified(
                 getClientPackageNameForSession(session),
                 origin,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index 4f1e1ef..1a7864c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -241,7 +241,12 @@
 
         // Window bounds adjustments are called here because we probe WebContents' width and height
         // from the native object.
+        // The condition including {@link #getSavedInstanceState} should be false iff the Activity
+        // has been recreated. If the Activity has been recreated, we should ignore the window
+        // features requested in the Intent as they should apply only for the initial launch of the
+        // Activity.
         if (getIntentDataProvider().getUiType() == CustomTabsUiType.POPUP
+                && getSavedInstanceState() == null
                 && ChromeFeatureList.isEnabled(
                         ChromeFeatureList.ANDROID_WINDOW_POPUP_RESIZE_AFTER_SPAWN)) {
             PopupCreator.adjustWindowBoundsToRequested(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
index 68e8111..8b264d8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
@@ -1127,7 +1127,7 @@
      * @param session Session extracted from the intent.
      * @param intent incoming intent.
      */
-    public void onHandledIntent(SessionHolder<?> session, Intent intent) {
+    public void onHandledIntent(@Nullable SessionHolder<?> session, Intent intent) {
         String url = IntentHandler.getUrlFromIntent(intent);
         if (TextUtils.isEmpty(url)) {
             return;
@@ -1169,7 +1169,7 @@
     }
 
     private void maybePreconnectToRedirectEndpoint(
-            SessionHolder<?> session, String url, Intent intent) {
+            @Nullable SessionHolder<?> session, String url, Intent intent) {
         // For the preconnection to not be a no-op, we need more than just the native library.
         if (!ChromeBrowserInitializer.getInstance().isFullBrowserInitialized()) {
             return;
@@ -1192,7 +1192,7 @@
 
     @VisibleForTesting
     @ParallelRequestStatus
-    int handleParallelRequest(SessionHolder<?> session, Intent intent) {
+    int handleParallelRequest(@Nullable SessionHolder<?> session, Intent intent) {
         int status = maybeStartParallelRequest(session, intent);
         RecordHistogram.recordEnumeratedHistogram(
                 "CustomTabs.ParallelRequestStatusOnStart",
@@ -1229,7 +1229,7 @@
      * @return Whether the request was started, with reason in case of failure.
      */
     private @ParallelRequestStatus int maybeStartParallelRequest(
-            SessionHolder<?> session, Intent intent) {
+            @Nullable SessionHolder<?> session, Intent intent) {
         ThreadUtils.assertOnUiThread();
 
         if (!intent.hasExtra(PARALLEL_REQUEST_URL_KEY)) return ParallelRequestStatus.NO_REQUEST;
@@ -1283,7 +1283,7 @@
      * @return Number of prefetch requests that have been sent.
      */
     @VisibleForTesting
-    int maybePrefetchResources(SessionHolder<?> session, Intent intent) {
+    int maybePrefetchResources(@Nullable SessionHolder<?> session, Intent intent) {
         ThreadUtils.assertOnUiThread();
 
         if (!mClientManager.getAllowResourcePrefetchForSession(session)) return 0;
@@ -1329,7 +1329,7 @@
      * @return Whether {@code session} can create a parallel request for a given {@code referrer}.
      */
     @VisibleForTesting
-    boolean canDoParallelRequest(SessionHolder<?> session, Uri referrer) {
+    boolean canDoParallelRequest(@Nullable SessionHolder<?> session, Uri referrer) {
         ThreadUtils.assertOnUiThread();
         Origin origin = Origin.create(referrer);
         if (origin == null) return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
index 87d694e..77730d9b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
@@ -669,6 +669,14 @@
     }
 
     private boolean checkIfTabReparentingParamsExistForIntent(Intent intent) {
+        // This condition should be true iff the Activity has been recreated. If the Activity has
+        // been recreated, we should ignore the Tab ID provided in the Intent -- the Tab will be
+        // restored using a different mechanism triggered by {@link
+        // ChromeActivity#performOnConfigurationChanged}. See crbug.com/448865648.
+        if (mSavedInstanceStateSupplier.get() != null) {
+            return false;
+        }
+
         int tabId = IntentHandler.getTabId(intent);
         AsyncTabParamsManager paramsManager = AsyncTabParamsManagerSingleton.getInstance();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaLauncherActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaLauncherActivity.java
index 5ae30556..158e97f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaLauncherActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaLauncherActivity.java
@@ -63,6 +63,7 @@
 
         try {
             startActivity(intent);
+            MediaViewerUtils.recordMediaLauncherActivityStarted();
         } catch (SecurityException e) {
             Log.w(TAG, "Cannot open content URI: " + contentUri, e);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java
index 18f8299e..2898692 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java
@@ -19,6 +19,7 @@
 import android.provider.Browser;
 import android.text.TextUtils;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.OptIn;
 import androidx.browser.customtabs.CustomTabsIntent;
 
@@ -26,6 +27,7 @@
 import org.chromium.base.IntentUtils;
 import org.chromium.base.ResettersForTesting;
 import org.chromium.base.SysUtils;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.build.annotations.NullMarked;
@@ -257,6 +259,37 @@
         }
     }
 
+    // These values are persisted to logs. Entries should not be renumbered and
+    // numeric values should never be reused.
+    @IntDef({
+        MediaLauncherActivityEnabledReason.LOW_END_DEVICE,
+                MediaLauncherActivityEnabledReason.ENTERPRISE,
+        MediaLauncherActivityEnabledReason.BOTH, MediaLauncherActivityEnabledReason.NEITHER
+    })
+    private @interface MediaLauncherActivityEnabledReason {
+        int LOW_END_DEVICE = 0;
+        int ENTERPRISE = 1;
+        int BOTH = 2;
+        int NEITHER = 3;
+        int COUNT = 4;
+    }
+
+    /** Records a histogram for MediaLauncherActivity metrics. */
+    public static void recordMediaLauncherActivityStarted() {
+        @MediaLauncherActivityEnabledReason int reason;
+        if (SysUtils.isLowEndDevice() && isEnterpriseManaged()) {
+            reason = MediaLauncherActivityEnabledReason.BOTH;
+        } else if (SysUtils.isLowEndDevice()) {
+            reason = MediaLauncherActivityEnabledReason.LOW_END_DEVICE;
+        } else if (isEnterpriseManaged()) {
+            reason = MediaLauncherActivityEnabledReason.ENTERPRISE;
+        } else {
+            reason = MediaLauncherActivityEnabledReason.NEITHER;
+        }
+        RecordHistogram.recordEnumeratedHistogram(
+                "MediaLauncherActivityStarted", reason, MediaLauncherActivityEnabledReason.COUNT);
+    }
+
     /** Force MediaLauncherActivity to be enabled for testing. */
     public static void forceEnableMediaLauncherActivityForTest() {
         sIsMediaLauncherActivityForceEnabledForTest = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationContentDetectionManager.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationContentDetectionManager.java
index dbd456f..b3b516f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationContentDetectionManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationContentDetectionManager.java
@@ -533,10 +533,13 @@
         }
     }
 
-    static void onUnsubscribeMaybeCommittedAfterWarning(String notificationId, String origin) {
+    static void onPreUnsubscribeMaybeCommittedAfterWarning(String notificationId, String origin) {
         // Record when the unsubscribe is completed.
         recordInteractionForUMAIfSuspicious(
                 origin, notificationId, SuspiciousNotificationWarningInteractions.UNSUBSCRIBE);
+    }
+
+    static void removeOriginFromSuspiciousMap(String origin) {
         sSuspiciousNotificationsMap.remove(origin);
         sWarningNotificationAttributesByOrigin.remove(origin);
     }
@@ -680,7 +683,7 @@
         mNotificationManager = notificationManager;
     }
 
-    private static boolean isNotificationSuspicious(String notificationId, String origin) {
+    static boolean isNotificationSuspicious(String notificationId, String origin) {
         if (sSuspiciousNotificationsMap.containsKey(origin)) {
             return sSuspiciousNotificationsMap.get(origin).contains(notificationId);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
index 7121a282..4ba050a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
@@ -159,7 +159,7 @@
         public final String webApkPackage;
         public final @Nullable String channelId;
 
-        public NotificationIdentifyingAttributes(
+        NotificationIdentifyingAttributes(
                 String notificationId,
                 @NotificationType int notificationType,
                 String origin,
@@ -284,7 +284,7 @@
                 notificationManager.cancel(attributes.notificationId, PLATFORM_ID);
                 // Check if the committed unsubscribe action is facilitated by a notification
                 // warning. If it is, then log metrics and cleanup static maps.
-                NotificationContentDetectionManager.onUnsubscribeMaybeCommittedAfterWarning(
+                NotificationContentDetectionManager.onPreUnsubscribeMaybeCommittedAfterWarning(
                         attributes.notificationId, attributes.origin);
                 return true;
             case NotificationConstants.ACTION_SHOW_ORIGINAL_NOTIFICATION:
@@ -312,8 +312,8 @@
                         SuspiciousNotificationWarningInteractions
                                 .REPORT_WARNED_NOTIFICATION_AS_SPAM);
                 // Reporting a warned notification also commits the unsubscribe action that is
-                // facilitated by the warning, so call this to log metrics and cleanup static maps.
-                NotificationContentDetectionManager.onUnsubscribeMaybeCommittedAfterWarning(
+                // facilitated by the warning, so call this to log metrics.
+                NotificationContentDetectionManager.onPreUnsubscribeMaybeCommittedAfterWarning(
                         attributes.notificationId, attributes.origin);
                 return true;
             case NotificationConstants.ACTION_REPORT_UNWARNED_NOTIFICATION_AS_SPAM:
@@ -1701,7 +1701,12 @@
                         identifyingAttributes.notificationType,
                         identifyingAttributes.origin,
                         identifyingAttributes.profileId,
-                        identifyingAttributes.incognito);
+                        identifyingAttributes.incognito,
+                        NotificationContentDetectionManager.isNotificationSuspicious(
+                                identifyingAttributes.notificationId,
+                                identifyingAttributes.origin));
+        NotificationContentDetectionManager.removeOriginFromSuspiciousMap(
+                identifyingAttributes.origin);
         var backups =
                 sOriginsWithProvisionallyRevokedPermissions.remove(identifyingAttributes.origin);
         NotificationUmaTracker.getInstance()
@@ -1909,7 +1914,8 @@
                 @NotificationType int notificationType,
                 @JniType("std::string") String origin,
                 @JniType("std::string") @Nullable String profileId,
-                boolean incognito);
+                boolean incognito,
+                boolean isSuspicious);
 
         void onNotificationShowOriginalNotification(
                 long nativeNotificationPlatformBridgeAndroid,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoCoordinator.java
index f910f9d..0b7a13e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoCoordinator.java
@@ -79,8 +79,11 @@
      * Shows the promo. The caller is responsible for all eligibility checks.
      *
      * @param featureType The {@link TipsNotificationsFeatureType} to show.
+     * @param featureActionRunnable The action to run for the associated feature type on positive
+     *     (settings) button click.
      */
-    public void showBottomSheet(@TipsNotificationsFeatureType int featureType) {
+    public void showBottomSheet(
+            @TipsNotificationsFeatureType int featureType, Runnable featureActionRunnable) {
         FeatureTipPromoData data = TipsUtils.getFeatureTipPromoDataForType(mContext, featureType);
         mPropertyModel.set(TipsPromoProperties.FEATURE_TIP_PROMO_DATA, data);
         mPropertyModel.set(TipsPromoProperties.CURRENT_SCREEN, ScreenType.MAIN_SCREEN);
@@ -90,6 +93,12 @@
                     mPropertyModel.set(
                             TipsPromoProperties.CURRENT_SCREEN, ScreenType.DETAIL_SCREEN);
                 });
+        mPropertyModel.set(
+                TipsPromoProperties.SETTINGS_BUTTON_CLICK_LISTENER,
+                (view) -> {
+                    mBottomSheetController.hideContent(mSheetContent, /* animate= */ true);
+                    featureActionRunnable.run();
+                });
         mBottomSheetController.requestShowContent(mSheetContent, /* animate= */ true);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoProperties.java
index 946dc1e..e3e6d03 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoProperties.java
@@ -59,8 +59,15 @@
     public static final WritableObjectPropertyKey<OnClickListener> DETAILS_BUTTON_CLICK_LISTENER =
             new WritableObjectPropertyKey<>();
 
+    /** Click listener for the settings button. */
+    public static final WritableObjectPropertyKey<OnClickListener> SETTINGS_BUTTON_CLICK_LISTENER =
+            new WritableObjectPropertyKey<>();
+
     public static final PropertyKey[] ALL_KEYS = {
-        FEATURE_TIP_PROMO_DATA, CURRENT_SCREEN, DETAILS_BUTTON_CLICK_LISTENER
+        FEATURE_TIP_PROMO_DATA,
+        CURRENT_SCREEN,
+        DETAILS_BUTTON_CLICK_LISTENER,
+        SETTINGS_BUTTON_CLICK_LISTENER
     };
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoViewBinder.java
index fe0fd5a..b466d836 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoViewBinder.java
@@ -36,6 +36,14 @@
             ButtonCompat detailsButton = view.findViewById(R.id.tips_promo_details_button);
             detailsButton.setOnClickListener(
                     model.get(TipsPromoProperties.DETAILS_BUTTON_CLICK_LISTENER));
+        } else if (key == TipsPromoProperties.SETTINGS_BUTTON_CLICK_LISTENER) {
+            ButtonCompat settingsButton = view.findViewById(R.id.tips_promo_settings_button);
+            settingsButton.setOnClickListener(
+                    model.get(TipsPromoProperties.SETTINGS_BUTTON_CLICK_LISTENER));
+            ButtonCompat detailsSettingsButton =
+                    view.findViewById(R.id.tips_promo_details_settings_button);
+            detailsSettingsButton.setOnClickListener(
+                    model.get(TipsPromoProperties.SETTINGS_BUTTON_CLICK_LISTENER));
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index 085bdb46..5a162b3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -72,6 +72,7 @@
 import org.chromium.chrome.browser.ntp_customization.NtpCustomizationConfigManager;
 import org.chromium.chrome.browser.ntp_customization.NtpCustomizationCoordinator;
 import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils;
+import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType;
 import org.chromium.chrome.browser.ntp_customization.edge_to_edge.TopInsetCoordinator;
 import org.chromium.chrome.browser.omnibox.OmniboxFocusReason;
 import org.chromium.chrome.browser.omnibox.OmniboxStub;
@@ -1173,7 +1174,7 @@
                 && !mIsTablet
                 && isInSingleUrlBarMode()
                 && NtpCustomizationConfigManager.getInstance().getBackgroundImageType()
-                        != NtpCustomizationUtils.NtpBackgroundImageType.DEFAULT;
+                        != NtpBackgroundImageType.DEFAULT;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImpl.java
index 4aea1118..051fa2a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImpl.java
@@ -160,14 +160,14 @@
                     PageContentProviderMetrics.recordPageProviderEvent(
                             RequestType.QUERY,
                             Format.PROTO,
-                            PageContentProviderEvent.QUERY_FAILED_INVALID_URL);
+                            PageContentProviderEvent.REQUEST_FAILED_INVALID_URL);
                     return cursor;
                 }
 
                 var format = match == URI_MATCH_TEXT_FORMAT ? Format.TEXT : Format.PROTO;
 
                 PageContentProviderMetrics.recordPageProviderEvent(
-                        RequestType.QUERY, format, PageContentProviderEvent.QUERY);
+                        RequestType.QUERY, format, PageContentProviderEvent.REQUEST_STARTED);
 
                 var pageContentFuture =
                         getPageContentsBytesAsync(invocationId, RequestType.QUERY, format);
@@ -183,20 +183,24 @@
                     } else {
                         setResultToCursor(cursor, sInvocationState.mInvokedUrl, pageContentBytes);
                     }
+                    PageContentProviderMetrics.recordPageProviderEvent(
+                            RequestType.QUERY,
+                            format,
+                            PageContentProviderEvent.REQUEST_SUCCEEDED_RETURNED_EXTRACTED);
                     return cursor;
 
                 } catch (InterruptedException e) {
                     PageContentProviderMetrics.recordPageProviderEvent(
                             RequestType.QUERY,
                             format,
-                            PageContentProviderEvent.QUERY_FAILED_INTERRUPTED);
+                            PageContentProviderEvent.REQUEST_FAILED_INTERRUPTED);
                     setErrorToCursor(cursor, "Extraction process was interrupted");
                     return cursor;
                 } catch (TimeoutException e) {
                     PageContentProviderMetrics.recordPageProviderEvent(
                             RequestType.QUERY,
                             format,
-                            PageContentProviderEvent.QUERY_FAILED_TIMED_OUT);
+                            PageContentProviderEvent.REQUEST_FAILED_TIMED_OUT);
                     setErrorToCursor(cursor, "Timed out during extraction");
                     return cursor;
                 } catch (ExecutionException e) {
@@ -206,7 +210,7 @@
                         PageContentProviderMetrics.recordPageProviderEvent(
                                 RequestType.QUERY,
                                 format,
-                                PageContentProviderEvent.QUERY_FAILED_EXCEPTION);
+                                PageContentProviderEvent.REQUEST_FAILED_EXCEPTION);
                         setErrorToCursor(cursor, "ExecutionException during extraction");
                     }
                     return cursor;
@@ -269,7 +273,7 @@
                     PageContentProviderMetrics.recordPageProviderEvent(
                             RequestType.OPEN_FILE,
                             Format.PROTO,
-                            PageContentProviderEvent.QUERY_FAILED_INVALID_URL);
+                            PageContentProviderEvent.REQUEST_FAILED_INVALID_URL);
                     throw new FileNotFoundException("Invalid URI");
                 }
 
@@ -283,12 +287,16 @@
                             pageContentFuture.get(
                                     PAGE_EXTRACTION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
                     recordExtractionStartToEndLatency(RequestType.OPEN_FILE, format);
+                    PageContentProviderMetrics.recordPageProviderEvent(
+                            RequestType.OPEN_FILE,
+                            format,
+                            PageContentProviderEvent.REQUEST_SUCCEEDED_RETURNED_EXTRACTED);
                     return createOutputFileDescriptorForBytes(pageContentBytes);
                 } catch (IOException e) {
                     PageContentProviderMetrics.recordPageProviderEvent(
                             RequestType.OPEN_FILE,
                             format,
-                            PageContentProviderEvent.QUERY_FAILED_EXCEPTION);
+                            PageContentProviderEvent.REQUEST_FAILED_EXCEPTION);
                     throw new FileNotFoundException("IO Exception");
                 } catch (ExecutionException e) {
                     if (e.getCause() != null && e.getCause().getMessage() != null) {
@@ -297,20 +305,20 @@
                         PageContentProviderMetrics.recordPageProviderEvent(
                                 RequestType.OPEN_FILE,
                                 format,
-                                PageContentProviderEvent.QUERY_FAILED_EXCEPTION);
+                                PageContentProviderEvent.REQUEST_FAILED_EXCEPTION);
                     }
                     throw new FileNotFoundException("ExecutionException during extraction");
                 } catch (InterruptedException e) {
                     PageContentProviderMetrics.recordPageProviderEvent(
                             RequestType.OPEN_FILE,
                             format,
-                            PageContentProviderEvent.QUERY_FAILED_INTERRUPTED);
+                            PageContentProviderEvent.REQUEST_FAILED_INTERRUPTED);
                     throw new FileNotFoundException("Extraction process was interrupted");
                 } catch (TimeoutException e) {
                     PageContentProviderMetrics.recordPageProviderEvent(
                             RequestType.OPEN_FILE,
                             format,
-                            PageContentProviderEvent.QUERY_FAILED_TIMED_OUT);
+                            PageContentProviderEvent.REQUEST_FAILED_TIMED_OUT);
                     throw new FileNotFoundException("Timed out during extraction");
                 }
             }
@@ -326,7 +334,7 @@
                     || sInvocationState == null
                     || !invocationId.equals(sInvocationState.mInvocationId)) {
                 PageContentProviderMetrics.recordPageProviderEvent(
-                        requestType, format, PageContentProviderEvent.QUERY_FAILED_INVALID_ID);
+                        requestType, format, PageContentProviderEvent.REQUEST_FAILED_INVALID_ID);
                 future.completeExceptionally(new Exception("Invalid ID"));
                 return future;
             }
@@ -360,13 +368,13 @@
                     PageContentProviderMetrics.recordPageProviderEvent(
                             requestType,
                             format,
-                            PageContentProviderEvent.QUERY_FAILED_TO_GET_CURRENT_TAB);
+                            PageContentProviderEvent.REQUEST_FAILED_TO_GET_CURRENT_TAB);
                     future.completeExceptionally(new Exception("Failed to get current tab"));
                 } else {
                     PageContentProviderMetrics.recordPageProviderEvent(
                             requestType,
                             format,
-                            PageContentProviderEvent.QUERY_FAILED_CURRENT_TAB_CHANGED);
+                            PageContentProviderEvent.REQUEST_FAILED_CURRENT_TAB_CHANGED);
                     future.completeExceptionally(
                             new Exception("Current tab changed before extraction"));
                 }
@@ -435,7 +443,7 @@
             if (sInvocationState != null && sInvocationState.mInvokedUrl.equals(url)) {
                 return sInvocationState.mInvocationId;
             } else {
-                clearCachedContent();
+                clearCachedContent(/* invocationId= */ null);
 
                 sInvocationState =
                         new PageContentInvocationState(
@@ -447,7 +455,7 @@
                 String invocationId = sInvocationState.mInvocationId;
                 PostTask.postDelayedTask(
                         TaskTraits.UI_DEFAULT,
-                        () -> clearCachedContentWithId(invocationId),
+                        () -> clearCachedContent(invocationId),
                         INVALIDATE_URI_DELAY_MS);
 
                 return sInvocationState.mInvocationId;
@@ -484,7 +492,11 @@
     public static @Nullable String getAssistContentStructuredDataForUrl(
             String url, ActivityTabProvider activityTabProvider, boolean isManagedProfile) {
         String invocationId = getIdForUrl(url, activityTabProvider);
-        if (invocationId == null) return null;
+        if (invocationId == null) {
+            PageContentProviderMetrics.recordPageProviderEvent(
+                    PageContentProviderEvent.GET_CONTENT_URI_FAILED);
+            return null;
+        }
 
         String structuredData;
 
@@ -501,9 +513,13 @@
                                                     buildProtoFormatUri(invocationId)))
                             .toString();
         } catch (JSONException e) {
+            PageContentProviderMetrics.recordPageProviderEvent(
+                    PageContentProviderEvent.GET_CONTENT_URI_FAILED);
             return null;
         }
 
+        PageContentProviderMetrics.recordPageProviderEvent(
+                PageContentProviderEvent.GET_CONTENT_URI_SUCCESS);
         return structuredData;
     }
 
@@ -561,17 +577,6 @@
                         buildProtoFormatUri(invocationId), Intent.FLAG_GRANT_READ_URI_PERMISSION);
     }
 
-    @VisibleForTesting
-    protected static void clearCachedContent() {
-        synchronized (sLock) {
-            if (sInvocationState != null) {
-                revokeAccessToId(sInvocationState.mInvocationId);
-            }
-
-            sInvocationState = null;
-        }
-    }
-
     /**
      * Clears the cached content related to an invocationId, meant to be used with a timer to clear
      * out data related to URIs that are never queried.
@@ -579,14 +584,23 @@
      * @param invocationId Invocation ID to clear, used to avoid clearing a newer request
      *     prematurely.
      */
-    private static void clearCachedContentWithId(String invocationId) {
+    @VisibleForTesting
+    protected static void clearCachedContent(@Nullable String invocationId) {
         synchronized (sLock) {
-            if (sInvocationState == null || !sInvocationState.mInvocationId.equals(invocationId)) {
+            if (sInvocationState == null) return;
+            if (invocationId != null && !sInvocationState.mInvocationId.equals(invocationId)) {
                 return;
             }
 
-            PageContentProviderMetrics.recordPageProviderEvent(PageContentProviderEvent.TIMEOUT);
-            clearCachedContent();
+            revokeAccessToId(sInvocationState.mInvocationId);
+            sInvocationState = null;
+            if (invocationId != null) {
+                PageContentProviderMetrics.recordPageProviderEvent(
+                        PageContentProviderEvent.URI_INVALIDATED_TIMEOUT);
+            } else {
+                PageContentProviderMetrics.recordPageProviderEvent(
+                        PageContentProviderEvent.URI_INVALIDATED_NEW_REQUEST);
+            }
         }
     }
 
@@ -611,7 +625,7 @@
                                                         requestType,
                                                         Format.TEXT,
                                                         PageContentProviderEvent
-                                                                .QUERY_FAILED_EMPTY_RESULT);
+                                                                .REQUEST_FAILED_EMPTY_RESULT);
                                                 pageContentFuture.completeExceptionally(
                                                         new Exception("Error during extraction"));
                                             } else {
@@ -646,7 +660,7 @@
                                                         requestType,
                                                         Format.PROTO,
                                                         PageContentProviderEvent
-                                                                .QUERY_FAILED_EMPTY_RESULT);
+                                                                .REQUEST_FAILED_EMPTY_RESULT);
                                                 pageContentFuture.completeExceptionally(
                                                         new Exception("Error during extraction"));
                                             } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImplUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImplUnitTest.java
index a06f49a..ada07b9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImplUnitTest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImplUnitTest.java
@@ -43,6 +43,9 @@
 import org.chromium.chrome.browser.content_extraction.InnerTextBridge;
 import org.chromium.chrome.browser.content_extraction.InnerTextBridgeJni;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.provider.PageContentProviderMetrics.Format;
+import org.chromium.chrome.browser.provider.PageContentProviderMetrics.PageContentProviderEvent;
+import org.chromium.chrome.browser.provider.PageContentProviderMetrics.RequestType;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.optimization_guide.content.PageContentProtoProviderBridge;
 import org.chromium.components.optimization_guide.content.PageContentProtoProviderBridgeJni;
@@ -86,7 +89,7 @@
         // In production code PageContentProvider must be called from the binder thread, disable
         // thread checks for these tests.
         ThreadUtils.hasSubtleSideEffectsSetThreadAssertsDisabledForTesting(true);
-        PageContentProviderImpl.clearCachedContent();
+        PageContentProviderImpl.clearCachedContent(/* invocationId= */ null);
         UkmRecorderJni.setInstanceForTesting(mUkmRecorderJniMock);
         mProvider = new PageContentProvider();
 
@@ -100,11 +103,17 @@
 
     @Test
     public void testInvalidUrl() {
+        var eventChecker =
+                getWatcherForEvent(
+                        RequestType.QUERY,
+                        Format.PROTO,
+                        PageContentProviderEvent.REQUEST_FAILED_INVALID_URL);
         var result =
                 mProvider.query(
                         Uri.parse("content://com.android.invalid/123456"), null, null, null, null);
 
         assertCursorContainsErrorMessage(result, "Invalid URI");
+        eventChecker.assertExpected();
     }
 
     @Test
@@ -112,6 +121,14 @@
         var structuredDataJson =
                 PageContentProviderImpl.getAssistContentStructuredDataForUrl(
                         JUnitTestGURLs.GOOGLE_URL.getSpec(), mActivityTabProvider, false);
+        var timeoutEventWatcher =
+                getWatcherForEvent(PageContentProviderEvent.URI_INVALIDATED_TIMEOUT);
+        var invalidIdEventWatcher =
+                getWatcherForEvent(
+                        RequestType.QUERY,
+                        Format.TEXT,
+                        PageContentProviderEvent.REQUEST_FAILED_INVALID_ID,
+                        PageContentProviderEvent.REQUEST_STARTED);
 
         var textContentUri = getMetadataFieldFromJson(structuredDataJson, "content_uri");
 
@@ -121,16 +138,20 @@
         var resultCursor = mProvider.query(Uri.parse(textContentUri), null, null, null, null);
 
         assertCursorContainsErrorMessage(resultCursor, "Invalid ID");
+        timeoutEventWatcher.assertExpected();
+        invalidIdEventWatcher.assertExpected();
     }
 
     @Test
     public void testGetAssistContentJson() {
+        var eventChecker = getWatcherForEvent(PageContentProviderEvent.GET_CONTENT_URI_SUCCESS);
         var structuredDataJson =
                 PageContentProviderImpl.getAssistContentStructuredDataForUrl(
                         JUnitTestGURLs.GOOGLE_URL.getSpec(), mActivityTabProvider, false);
 
         var textContentUri = getMetadataFieldFromJson(structuredDataJson, "content_uri");
         assertNotNull(textContentUri);
+        eventChecker.assertExpected();
     }
 
     @Test
@@ -140,6 +161,12 @@
         var structuredDataJson =
                 PageContentProviderImpl.getAssistContentStructuredDataForUrl(
                         JUnitTestGURLs.GOOGLE_URL.getSpec(), mActivityTabProvider, false);
+        var eventChecker =
+                getWatcherForEvent(
+                        RequestType.QUERY,
+                        Format.TEXT,
+                        PageContentProviderEvent.REQUEST_STARTED,
+                        PageContentProviderEvent.REQUEST_SUCCEEDED_RETURNED_EXTRACTED);
 
         var contentUri = getMetadataFieldFromJson(structuredDataJson, "content_uri");
         // Wait 300ms between creating URI and querying it.
@@ -166,6 +193,7 @@
                 JUnitTestGURLs.GOOGLE_URL.getSpec(),
                 /* textContents= */ "Page contents!");
         verify(mInnerTextNatives).getInnerText(eq(mRenderFrameHost), any());
+        eventChecker.assertExpected();
     }
 
     @Test
@@ -174,11 +202,18 @@
         var structuredDataJson =
                 PageContentProviderImpl.getAssistContentStructuredDataForUrl(
                         JUnitTestGURLs.GOOGLE_URL.getSpec(), mActivityTabProvider, false);
+        var eventChecker =
+                getWatcherForEvent(
+                        RequestType.QUERY,
+                        Format.TEXT,
+                        PageContentProviderEvent.REQUEST_STARTED,
+                        PageContentProviderEvent.REQUEST_FAILED_EMPTY_RESULT);
 
         var contentUri = getMetadataFieldFromJson(structuredDataJson, "content_uri");
         mFakeTimeTestRule.advanceMillis(300);
         Cursor resultCursor = mProvider.query(Uri.parse(contentUri), null, null, null, null);
         assertCursorContainsErrorMessage(resultCursor, "Error during extraction");
+        eventChecker.assertExpected();
     }
 
     @Test
@@ -186,6 +221,12 @@
         var nodeContents = "Page contents!";
         var apcProto = getAnnotatedPageContentsProto(nodeContents);
         setProtoContentExtractionResult(apcProto, 100);
+        var eventChecker =
+                getWatcherForEvent(
+                        RequestType.QUERY,
+                        Format.PROTO,
+                        PageContentProviderEvent.REQUEST_STARTED,
+                        PageContentProviderEvent.REQUEST_SUCCEEDED_RETURNED_EXTRACTED);
 
         var structuredDataJson =
                 PageContentProviderImpl.getAssistContentStructuredDataForUrl(
@@ -196,6 +237,22 @@
 
         assertProtoCursorContainsValues(
                 resultCursor, JUnitTestGURLs.GOOGLE_URL.getSpec(), apcProto.toByteArray());
+        eventChecker.assertExpected();
+    }
+
+    private HistogramWatcher getWatcherForEvent(@PageContentProviderEvent int event) {
+        return HistogramWatcher.newSingleRecordWatcher(
+                "Android.AssistContent.WebPageContentProvider.Events", event);
+    }
+
+    private HistogramWatcher getWatcherForEvent(
+            @RequestType int requestType,
+            @Format int format,
+            @PageContentProviderEvent int... events) {
+        var histogramName =
+                PageContentProviderMetrics.concatenateTypeAndFormatToHistogramName(
+                        "Android.AssistContent.WebPageContentProvider.Events", requestType, format);
+        return HistogramWatcher.newBuilder().expectIntRecords(histogramName, events).build();
     }
 
     private String getMetadataFieldFromJson(String jsonString, String fieldName) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderMetrics.java
index 3aa9e70..fdb70baa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderMetrics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderMetrics.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.provider;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.build.annotations.NullMarked;
@@ -31,34 +32,39 @@
 
     @IntDef({
         PageContentProviderEvent.GET_CONTENT_URI_FAILED,
-        PageContentProviderEvent.QUERY,
-        PageContentProviderEvent.QUERY_FAILED_CURRENT_TAB_CHANGED,
-        PageContentProviderEvent.QUERY_FAILED_INVALID_URL,
-        PageContentProviderEvent.QUERY_FAILED_INVALID_ID,
-        PageContentProviderEvent.QUERY_FAILED_TO_GET_CURRENT_TAB,
-        PageContentProviderEvent.QUERY_SUCCEEDED_RETURNED_EXTRACTED,
-        PageContentProviderEvent.QUERY_FAILED_EMPTY_RESULT,
-        PageContentProviderEvent.QUERY_FAILED_INTERRUPTED,
-        PageContentProviderEvent.QUERY_FAILED_TIMED_OUT,
-        PageContentProviderEvent.QUERY_FAILED_EXCEPTION,
-        PageContentProviderEvent.TIMEOUT,
+        PageContentProviderEvent.REQUEST_STARTED,
+        PageContentProviderEvent.REQUEST_FAILED_CURRENT_TAB_CHANGED,
+        PageContentProviderEvent.REQUEST_FAILED_INVALID_URL,
+        PageContentProviderEvent.REQUEST_FAILED_INVALID_ID,
+        PageContentProviderEvent.REQUEST_FAILED_TO_GET_CURRENT_TAB,
+        PageContentProviderEvent.REQUEST_SUCCEEDED_RETURNED_EXTRACTED,
+        PageContentProviderEvent.REQUEST_FAILED_EMPTY_RESULT,
+        PageContentProviderEvent.REQUEST_FAILED_INTERRUPTED,
+        PageContentProviderEvent.REQUEST_FAILED_TIMED_OUT,
+        PageContentProviderEvent.REQUEST_FAILED_EXCEPTION,
+        PageContentProviderEvent.GET_CONTENT_URI_FAILED,
+        PageContentProviderEvent.GET_CONTENT_URI_SUCCESS,
+        PageContentProviderEvent.URI_INVALIDATED_NEW_REQUEST,
+        PageContentProviderEvent.URI_INVALIDATED_TIMEOUT,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PageContentProviderEvent {
 
         int GET_CONTENT_URI_FAILED = 0;
-        int QUERY = 1;
-        int QUERY_FAILED_CURRENT_TAB_CHANGED = 2;
-        int QUERY_FAILED_INVALID_URL = 3;
-        int QUERY_FAILED_INVALID_ID = 4;
-        int QUERY_FAILED_TO_GET_CURRENT_TAB = 5;
-        int QUERY_SUCCEEDED_RETURNED_EXTRACTED = 7;
-        int QUERY_FAILED_EMPTY_RESULT = 9;
-        int TIMEOUT = 12;
-        int QUERY_FAILED_INTERRUPTED = 13;
-        int QUERY_FAILED_TIMED_OUT = 14;
-        int QUERY_FAILED_EXCEPTION = 15;
-        int NUM_ENTRIES = 16;
+        int REQUEST_STARTED = 1;
+        int REQUEST_FAILED_CURRENT_TAB_CHANGED = 2;
+        int REQUEST_FAILED_INVALID_URL = 3;
+        int REQUEST_FAILED_INVALID_ID = 4;
+        int REQUEST_FAILED_TO_GET_CURRENT_TAB = 5;
+        int REQUEST_SUCCEEDED_RETURNED_EXTRACTED = 7;
+        int REQUEST_FAILED_EMPTY_RESULT = 9;
+        int URI_INVALIDATED_TIMEOUT = 12;
+        int REQUEST_FAILED_INTERRUPTED = 13;
+        int REQUEST_FAILED_TIMED_OUT = 14;
+        int REQUEST_FAILED_EXCEPTION = 15;
+        int GET_CONTENT_URI_SUCCESS = 16;
+        int URI_INVALIDATED_NEW_REQUEST = 17;
+        int NUM_ENTRIES = 18;
     }
 
     public static void recordPageProviderEvent(@PageContentProviderEvent int event) {
@@ -141,7 +147,8 @@
         RecordHistogram.recordMediumTimesHistogram(histogramName, duration);
     }
 
-    private static String concatenateTypeAndFormatToHistogramName(
+    @VisibleForTesting
+    static String concatenateTypeAndFormatToHistogramName(
             String histogram, @RequestType int requestType, @Format int format) {
         return histogram
                 + '.'
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/MultiColumnSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/MultiColumnSettings.java
index 4542f90..89ff774 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/MultiColumnSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/MultiColumnSettings.java
@@ -160,7 +160,7 @@
         }
     }
 
-    int getHeaderPanelWidthPx() {
+    public int getHeaderPanelWidthPx() {
         return mHeaderPanelWidthPx;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
index d414187..494e30c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
@@ -14,15 +14,11 @@
 import android.content.res.Configuration;
 import android.graphics.Color;
 import android.os.Bundle;
-import android.os.Handler;
-import android.view.Gravity;
 import android.view.KeyEvent;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
@@ -52,6 +48,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.profiles.ProfileManagerUtils;
+import org.chromium.chrome.browser.settings.search.SettingsSearchCoordinator;
 import org.chromium.chrome.browser.ui.device_lock.MissingDeviceLockLauncher;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarManageable;
@@ -155,9 +152,7 @@
 
     private final Map<Fragment, ContainmentItemDecoration> mItemDecorations = new HashMap<>();
 
-    // True if multiple-column Fragment is activated. Both the window width and the feature flag
-    // condition should be met.
-    private boolean mUseMultiColumn;
+    private @Nullable SettingsSearchCoordinator mSearchCoordinator;
 
     @SuppressLint("InlinedApi")
     @Override
@@ -245,7 +240,6 @@
         Toolbar actionBar = findViewById(R.id.action_bar);
         setSupportActionBar(actionBar);
         assumeNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
-        if (ChromeFeatureList.sSearchInSettings.isEnabled()) initializeSearchUi();
 
         mIsNewlyCreated = savedInstanceState == null;
 
@@ -271,6 +265,12 @@
             }
         }
 
+        if (ChromeFeatureList.sSearchInSettings.isEnabled()) {
+            mSearchCoordinator =
+                    new SettingsSearchCoordinator(
+                            this, this::getUseMultiColumn, mMultiColumnSettings);
+            mSearchCoordinator.initializeSearchUi();
+        }
         setStatusBarColor();
         initBottomSheet();
 
@@ -284,110 +284,11 @@
         }
     }
 
-    private void initializeSearchUi() {
-        // TODO(jinsukkim): Factor out to its own MVC when the code size grows.
-        mUseMultiColumn = getUseMultiColumn();
-        Toolbar actionBar = findViewById(R.id.action_bar);
-        ViewGroup appBar = findViewById(R.id.app_bar_layout);
-        ViewGroup searchBoxParent = mUseMultiColumn ? actionBar : appBar;
-        LayoutInflater.from(this).inflate(R.layout.settings_search_box, searchBoxParent, true);
-        LayoutInflater.from(this).inflate(R.layout.settings_search_query, actionBar, true);
-        View searchBox = findViewById(R.id.search_box);
-        View queryContainer = findViewById(R.id.search_query_container);
-        if (mUseMultiColumn) {
-            // Adjust the view width after the Fragment layout is completed.
-            new Handler().post(this::updateDetailPanelWidth);
-        }
-        searchBox.setOnClickListener(
-                v -> {
-                    searchBox.setVisibility(View.GONE);
-                    queryContainer.setVisibility(View.VISIBLE);
-                    if (!mUseMultiColumn) {
-                        assumeNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(false);
-                    }
-                    // TODO(jinsukkim): Initialize search query widget.
-                });
-        View backToSettings = findViewById(R.id.back_arrow_icon);
-        backToSettings.setOnClickListener(
-                v -> {
-                    queryContainer.setVisibility(View.GONE);
-                    searchBox.setVisibility(View.VISIBLE);
-                    if (!mUseMultiColumn) {
-                        assumeNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
-                    }
-                    getSettingsFragmentManager().popBackStack();
-                    // TODO(jinsukkim): Complete back action.
-                });
-        actionBar.setOverflowIcon(null);
-    }
-
-    private FragmentManager getSettingsFragmentManager() {
-        if (mUseMultiColumn) {
-            return assumeNonNull(mMultiColumnSettings).getChildFragmentManager();
-        } else {
-            return getSupportFragmentManager();
-        }
-    }
-
-    private void updateDetailPanelWidth() {
-        assert mUseMultiColumn : "Should be called in multi-column mode only.";
-
-        var windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(this);
-        int endPaddingPx =
-                getResources().getDimensionPixelSize(R.dimen.settings_detail_panel_end_padding);
-        int headerWidthPx = assumeNonNull(mMultiColumnSettings).getHeaderPanelWidthPx();
-        int detailViewWidthPx = windowMetrics.getBounds().width() - headerWidthPx - endPaddingPx;
-        View searchBox = findViewById(R.id.search_box);
-        var lp = (Toolbar.LayoutParams) searchBox.getLayoutParams();
-        lp.width = detailViewWidthPx;
-        lp.gravity = Gravity.END;
-        searchBox.setLayoutParams(lp);
-        View queryContainer = findViewById(R.id.search_query_container);
-        LayoutParams qlp = queryContainer.getLayoutParams();
-        qlp.width = detailViewWidthPx;
-        queryContainer.setLayoutParams(qlp);
-    }
-
     @Override
     public void onConfigurationChanged(@NonNull Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         updateContainmentForMultiColumnLayout();
-
-        if (!ChromeFeatureList.sSearchInSettings.isEnabled()) {
-            return;
-        }
-
-        boolean useMultiColumn = getUseMultiColumn();
-
-        if (useMultiColumn == mUseMultiColumn) {
-            if (mUseMultiColumn) new Handler().post(this::updateDetailPanelWidth);
-            return;
-        }
-
-        mUseMultiColumn = useMultiColumn;
-        View searchBox = findViewById(R.id.search_box);
-        ViewGroup searchBoxParent =
-                findViewById(useMultiColumn ? R.id.app_bar_layout : R.id.action_bar);
-        searchBoxParent.removeView(searchBox);
-        new Handler().post(() -> switchSearchUiLayout(searchBox));
-    }
-
-    private void switchSearchUiLayout(View searchBox) {
-        if (mUseMultiColumn) {
-            ViewGroup actionBar = findViewById(R.id.action_bar);
-            actionBar.addView(searchBox);
-            updateDetailPanelWidth();
-        } else {
-            ViewGroup appBarLayout = findViewById(R.id.app_bar_layout);
-            appBarLayout.addView(searchBox);
-            View queryContainer = findViewById(R.id.search_query_container);
-            LayoutParams lp = searchBox.getLayoutParams();
-            lp.width = LayoutParams.MATCH_PARENT;
-            searchBox.setLayoutParams(lp);
-            lp = queryContainer.getLayoutParams();
-            lp.width = LayoutParams.MATCH_PARENT;
-            queryContainer.setLayoutParams(lp);
-        }
+        if (mSearchCoordinator != null) mSearchCoordinator.onConfigurationChanged(newConfig);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/search/SettingsSearchCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/search/SettingsSearchCoordinator.java
new file mode 100644
index 0000000..dd1af1bb
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/search/SettingsSearchCoordinator.java
@@ -0,0 +1,163 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.settings.search;
+
+import static org.chromium.build.NullUtil.assumeNonNull;
+
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.fragment.app.FragmentManager;
+import androidx.window.layout.WindowMetricsCalculator;
+
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.settings.MultiColumnSettings;
+
+import java.util.function.BooleanSupplier;
+
+/** The coordinator of search in Settings. TODO(jinsukkim): Build a proper MVC structure. */
+@NullMarked
+public class SettingsSearchCoordinator {
+    private final AppCompatActivity mActivity;
+    private final BooleanSupplier mUseMultiColumnSupplier;
+    private @Nullable final MultiColumnSettings mMultiColumnSettings;
+
+    // True if multiple-column Fragment is activated. Both the window width and the feature flag
+    // condition should be met.
+    private boolean mUseMultiColumn;
+
+    /**
+     * @param activity {@link SettingsActivity} object
+     * @param useMultiColumnSupplier Supplier telling us whether the multi-column mode is on
+     * @param multiColumnSettings {@link MultiColumnSettings} Fragment. Can be {@code null} unless
+     *     the multi-column settings feature is enabled.
+     */
+    public SettingsSearchCoordinator(
+            AppCompatActivity activity,
+            BooleanSupplier useMultiColumnSupplier,
+            @Nullable MultiColumnSettings multiColumnSettings) {
+        mActivity = activity;
+        mUseMultiColumnSupplier = useMultiColumnSupplier;
+        mMultiColumnSettings = multiColumnSettings;
+    }
+
+    /** Initializes search UI, sets up listeners, backpress action handler, etc. */
+    public void initializeSearchUi() {
+        mUseMultiColumn = mUseMultiColumnSupplier.getAsBoolean();
+        Toolbar actionBar = mActivity.findViewById(R.id.action_bar);
+        ViewGroup appBar = mActivity.findViewById(R.id.app_bar_layout);
+        ViewGroup searchBoxParent = mUseMultiColumn ? actionBar : appBar;
+        LayoutInflater.from(mActivity).inflate(R.layout.settings_search_box, searchBoxParent, true);
+        LayoutInflater.from(mActivity).inflate(R.layout.settings_search_query, actionBar, true);
+        View searchBox = mActivity.findViewById(R.id.search_box);
+        View queryContainer = mActivity.findViewById(R.id.search_query_container);
+        if (mUseMultiColumn) {
+            // Adjust the view width after the Fragment layout is completed.
+            new Handler().post(this::updateDetailPanelWidth);
+        }
+        searchBox.setOnClickListener(
+                v -> {
+                    searchBox.setVisibility(View.GONE);
+                    queryContainer.setVisibility(View.VISIBLE);
+                    if (!mUseMultiColumn) {
+                        assumeNonNull(mActivity.getSupportActionBar())
+                                .setDisplayHomeAsUpEnabled(false);
+                    }
+                    // TODO(jinsukkim): Initialize search query widget.
+                });
+        View backToSettings = mActivity.findViewById(R.id.back_arrow_icon);
+        backToSettings.setOnClickListener(
+                v -> {
+                    queryContainer.setVisibility(View.GONE);
+                    searchBox.setVisibility(View.VISIBLE);
+                    if (!mUseMultiColumn) {
+                        assumeNonNull(mActivity.getSupportActionBar())
+                                .setDisplayHomeAsUpEnabled(true);
+                    }
+                    getSettingsFragmentManager().popBackStack();
+                    // TODO(jinsukkim): Complete back action.
+                });
+        actionBar.setOverflowIcon(null);
+    }
+
+    private FragmentManager getSettingsFragmentManager() {
+        if (mUseMultiColumn) {
+            return assumeNonNull(mMultiColumnSettings).getChildFragmentManager();
+        } else {
+            return mActivity.getSupportFragmentManager();
+        }
+    }
+
+    private void updateDetailPanelWidth() {
+        assert mUseMultiColumn : "Should be called in multi-column mode only.";
+
+        var windowMetrics =
+                WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(mActivity);
+        int endPaddingPx =
+                mActivity
+                        .getResources()
+                        .getDimensionPixelSize(R.dimen.settings_detail_panel_end_padding);
+        int headerWidthPx = assumeNonNull(mMultiColumnSettings).getHeaderPanelWidthPx();
+        int detailViewWidthPx = windowMetrics.getBounds().width() - headerWidthPx - endPaddingPx;
+        View searchBox = mActivity.findViewById(R.id.search_box);
+        var lp = (Toolbar.LayoutParams) searchBox.getLayoutParams();
+        lp.width = detailViewWidthPx;
+        lp.gravity = Gravity.END;
+        searchBox.setLayoutParams(lp);
+        View queryContainer = mActivity.findViewById(R.id.search_query_container);
+        LayoutParams qlp = queryContainer.getLayoutParams();
+        qlp.width = detailViewWidthPx;
+        queryContainer.setLayoutParams(qlp);
+    }
+
+    /**
+     * Updates the UI layout for the changes in column mode/window width.
+     *
+     * @see {@link Activity#onConfigurationChanged(Configuration)}.
+     */
+    public void onConfigurationChanged(@NonNull Configuration newConfig) {
+        boolean useMultiColumn = mUseMultiColumnSupplier.getAsBoolean();
+
+        if (useMultiColumn == mUseMultiColumn) {
+            if (mUseMultiColumn) new Handler().post(this::updateDetailPanelWidth);
+            return;
+        }
+
+        mUseMultiColumn = mUseMultiColumnSupplier.getAsBoolean();
+        View searchBox = mActivity.findViewById(R.id.search_box);
+        ViewGroup searchBoxParent =
+                mActivity.findViewById(useMultiColumn ? R.id.app_bar_layout : R.id.action_bar);
+        searchBoxParent.removeView(searchBox);
+        new Handler().post(() -> switchSearchUiLayout(searchBox));
+    }
+
+    private void switchSearchUiLayout(View searchBox) {
+        if (mUseMultiColumn) {
+            ViewGroup actionBar = mActivity.findViewById(R.id.action_bar);
+            actionBar.addView(searchBox);
+            updateDetailPanelWidth();
+        } else {
+            ViewGroup appBarLayout = mActivity.findViewById(R.id.app_bar_layout);
+            appBarLayout.addView(searchBox);
+            View queryContainer = mActivity.findViewById(R.id.search_query_container);
+            LayoutParams lp = searchBox.getLayoutParams();
+            lp.width = LayoutParams.MATCH_PARENT;
+            searchBox.setLayoutParams(lp);
+            lp = queryContainer.getLayoutParams();
+            lp.width = LayoutParams.MATCH_PARENT;
+            queryContainer.setLayoutParams(lp);
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateClientImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateClientImpl.java
index cbb48ce..876c5ba3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateClientImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateClientImpl.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.tab;
 
+import static org.chromium.build.NullUtil.assertNonNull;
 import static org.chromium.build.NullUtil.assumeNonNull;
 
 import android.app.Activity;
@@ -222,7 +223,7 @@
 
     private Runnable cleanupPendingTabClosure() {
         final boolean isChromeTabbedActivityRunning =
-                LaunchIntentDispatcher.chromeTabbedTaskExists(getActivity());
+                LaunchIntentDispatcher.chromeTabbedTaskExists(assertNonNull(getActivity()));
         return () -> {
             if (mTab.didCloseWhileDetached()) {
                 PostTask.postTask(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
index ae7431e8..bb877bf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
@@ -10,7 +10,6 @@
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
-import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Build;
 import android.text.TextUtils;
@@ -1841,64 +1840,6 @@
         }
     }
 
-    /** This is currently used when restoring tabs, and by DOMDistiller */
-    @CalledByNative
-    void swapWebContents(WebContents webContents, boolean didStartLoad, boolean didFinishLoad) {
-        boolean hasWebContents = mContentView != null && mWebContents != null;
-        assumeNonNull(mContentView);
-        Rect original =
-                hasWebContents
-                        ? new Rect(0, 0, mContentView.getWidth(), mContentView.getHeight())
-                        : new Rect();
-        for (TabObserver observer : mObservers) observer.webContentsWillSwap(this);
-        if (hasWebContents) {
-            assumeNonNull(mWebContents);
-            mWebContents.updateWebContentsVisibility(Visibility.HIDDEN);
-        }
-        Context appContext = ContextUtils.getApplicationContext();
-        Rect bounds = original.isEmpty() ? TabUtils.estimateContentSize(appContext) : null;
-        if (bounds != null) original.set(bounds);
-
-        if (hasWebContents) {
-            assumeNonNull(mWebContents);
-            mWebContents.setFocus(false);
-        }
-        destroyWebContents(false /* do not delete native web contents */);
-        hideNativePage(
-                false,
-                () -> {
-                    // Size of the new content is zero at this point. Set the view size in advance
-                    // so that next onShow() call won't send a resize message with zero size
-                    // to the renderer process. This prevents the size fluttering that may confuse
-                    // Blink and break rendered result (see http://crbug.com/340987).
-                    webContents.setSize(original.width(), original.height());
-
-                    if (bounds != null) {
-                        assert mNativeTabAndroid != 0;
-                        TabImplJni.get()
-                                .onPhysicalBackingSizeChanged(
-                                        mNativeTabAndroid,
-                                        webContents,
-                                        bounds.right,
-                                        bounds.bottom);
-                    }
-                    initWebContents(webContents);
-                    updateWebContentsVisibility();
-                });
-
-        if (didStartLoad) {
-            // Simulate the PAGE_LOAD_STARTED notification that we did not get.
-            didStartPageLoad(getUrl());
-
-            // Simulate the PAGE_LOAD_FINISHED notification that we did not get.
-            if (didFinishLoad) didFinishPageLoad(getUrl());
-        }
-
-        for (TabObserver observer : mObservers) {
-            observer.onWebContentsSwapped(this, didStartLoad, didFinishLoad);
-        }
-    }
-
     /** Builds the native counterpart to this class. */
     private void initializeNative() {
         if (mNativeTabAndroid == 0) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAdaptiveToolbarBehavior.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAdaptiveToolbarBehavior.java
index 643c6634..1d862da 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAdaptiveToolbarBehavior.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAdaptiveToolbarBehavior.java
@@ -28,6 +28,7 @@
 import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarButtonVariant;
 import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarFeatures;
 import org.chromium.chrome.browser.toolbar.adaptive.OptionalNewTabButtonController;
+import org.chromium.chrome.browser.toolbar.top.tab_strip.StripVisibilityState;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.modaldialog.ModalDialogManager;
@@ -49,6 +50,7 @@
             mGroupSuggestionsButtonControllerSupplier;
     private final Supplier<TabModelSelector> mTabModelSelectorSupplier;
     private final Supplier<ModalDialogManager> mModalDialogManagerSupplier;
+    private final ObservableSupplier<@StripVisibilityState Integer> mTabStripVisibilitySupplier;
 
     public TabbedAdaptiveToolbarBehavior(
             Context context,
@@ -60,7 +62,8 @@
             Runnable registerVoiceSearchRunnable,
             Supplier<GroupSuggestionsButtonController> groupSuggestionsButtonController,
             Supplier<TabModelSelector> tabModelSelectorSupplier,
-            Supplier<ModalDialogManager> modalDialogManagerSupplier) {
+            Supplier<ModalDialogManager> modalDialogManagerSupplier,
+            ObservableSupplier<@StripVisibilityState Integer> tabStripVisibilitySupplier) {
         mContext = context;
         mActivityLifecycleDispatcher = activityLifecycleDispatcher;
         mTabCreatorManagerSupplier = tabCreatorManagerSupplier;
@@ -71,6 +74,7 @@
         mGroupSuggestionsButtonControllerSupplier = groupSuggestionsButtonController;
         mTabModelSelectorSupplier = tabModelSelectorSupplier;
         mModalDialogManagerSupplier = modalDialogManagerSupplier;
+        mTabStripVisibilitySupplier = tabStripVisibilitySupplier;
     }
 
     @Override
@@ -84,7 +88,8 @@
                         mActivityLifecycleDispatcher,
                         mTabCreatorManagerSupplier,
                         mActivityTabProvider,
-                        trackerSupplier);
+                        trackerSupplier,
+                        mTabStripVisibilitySupplier);
         controller.addButtonVariant(AdaptiveToolbarButtonVariant.NEW_TAB, newTabButton);
         var addToBookmarks =
                 new AddToBookmarksToolbarButtonController(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index 3a2e3c7..87d753cd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -999,7 +999,8 @@
                 () -> addVoiceSearchAdaptiveButton(trackerSupplier),
                 groupSuggestionsButtonControllerSupplier,
                 mTabModelSelectorSupplier,
-                mModalDialogManagerSupplier);
+                mModalDialogManagerSupplier,
+                mTabStripVisibilitySupplier);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java
index faddaa51..1f170882 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java
@@ -611,8 +611,25 @@
     }
 
     @Override
-    public void pinTab(int tabId) {
-        updatePinnedState(tabId, /* isPinned= */ true);
+    public void pinTab(
+            int tabId,
+            boolean showUngroupDialog,
+            @Nullable TabModelActionListener tabModelActionListener) {
+        Tab tab = getTabById(tabId);
+        if (tab == null) return;
+        if (tab.getIsPinned()) return;
+
+        TabPinnerActionListener listener =
+                new TabPinnerActionListener(
+                        () -> updatePinnedState(tabId, /* isPinned= */ true),
+                        tabModelActionListener);
+        getTabUngrouper()
+                .ungroupTabs(
+                        Collections.singletonList(tab),
+                        /* trailing= */ true,
+                        showUngroupDialog,
+                        listener);
+        listener.pinIfCollaborationDialogShown();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
index fd70a27..cc81cf2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
@@ -407,20 +407,40 @@
     }
 
     @Override
-    public void pinTab(int tabId) {
+    public void pinTab(
+            int tabId,
+            boolean showUngroupDialog,
+            @Nullable TabModelActionListener tabModelActionListener) {
+        Tab eligibleTabToPin = getEligibleTabToPin(tabId);
+        if (eligibleTabToPin == null) return;
+
+        TabPinnerActionListener listener =
+                new TabPinnerActionListener(() -> doPin(tabId), tabModelActionListener);
+        getTabUngrouper()
+                .ungroupTabs(
+                        Collections.singletonList(eligibleTabToPin),
+                        /* trailing= */ true,
+                        showUngroupDialog,
+                        listener);
+        listener.pinIfCollaborationDialogShown();
+    }
+
+    private @Nullable Tab getEligibleTabToPin(int tabId) {
+        Tab tab = getTabById(tabId);
+        if (tab == null) return null;
+
+        if (tab.getIsPinned()) return null;
+
+        return tab;
+    }
+
+    private void doPin(int tabId) {
+        Tab tab = getEligibleTabToPin(tabId);
+        if (tab == null) return;
+
         int availableIndex = findFirstNonPinnedTabIndex();
         if (availableIndex == mTabs.size()) return;
 
-        Tab tab = getTabById(tabId);
-        if (tab == null) return;
-
-        if (tab.getIsPinned()) return;
-
-        // Call #notifyWillChangePinState before #moveTab. The notify step triggers
-        // TabGroupModelFilterImpl#willChangePinState to ungroup the tab prior to pinning.
-        // #moveTab typically kicks off StripLayoutHelper#rebuildStripView. If rebuild runs
-        // before the tab is removed from its group, the strip can treat the group as split,
-        // miscount groups, and hit an out-of-bounds.
         notifyWillChangeInPinState(tab);
         tab.setIsPinned(true);
         recordPinTimestamp(tab);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
index 8b4c124..72e7ce4a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
@@ -543,7 +543,7 @@
         @TabId int tabId = tab.getId();
         if (tabId == Tab.INVALID_TAB_ID) return;
 
-        pinTab(tabId);
+        pinTab(tabId, /* showUngroupDialog= */ false);
     }
 
     @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPinnerActionListener.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPinnerActionListener.java
new file mode 100644
index 0000000..832720e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPinnerActionListener.java
@@ -0,0 +1,97 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tabmodel;
+
+import static org.chromium.build.NullUtil.assumeNonNull;
+
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.tabmodel.TabModelActionListener.DialogType;
+import org.chromium.components.browser_ui.widget.ActionConfirmationResult;
+
+/**
+ * A {@link TabModelActionListener} that is used as part of handling an ungroup before pinning a
+ * tab.
+ */
+@NullMarked
+/*package*/ class TabPinnerActionListener implements TabModelActionListener {
+    private final @Nullable TabModelActionListener mInnerListener;
+    private final OnceRunnable mDoPinRunnable;
+    private boolean mCollaborationDialogShown;
+
+    /** A {@link Runnable} that is only run once. */
+    private static class OnceRunnable implements Runnable {
+        private @Nullable Runnable mRunnable;
+
+        OnceRunnable(Runnable runnable) {
+            mRunnable = runnable;
+        }
+
+        @Override
+        public void run() {
+            assumeNonNull(mRunnable);
+            mRunnable.run();
+            mRunnable = null;
+        }
+    }
+
+    public TabPinnerActionListener(
+            Runnable doPinRunnable, @Nullable TabModelActionListener innerListener) {
+        mDoPinRunnable = new OnceRunnable(doPinRunnable);
+        mInnerListener = innerListener;
+    }
+
+    /**
+     * The collaboration dialog was shown so the ungroup has already happened. We should run the pin
+     * operation now.
+     */
+    public void pinIfCollaborationDialogShown() {
+        if (mCollaborationDialogShown) {
+            mDoPinRunnable.run();
+        }
+    }
+
+    @Override
+    public void willPerformActionOrShowDialog(@DialogType int dialogType, boolean willSkipDialog) {
+        if (mInnerListener != null) {
+            mInnerListener.willPerformActionOrShowDialog(dialogType, willSkipDialog);
+        }
+        mCollaborationDialogShown = dialogType == DialogType.COLLABORATION && !willSkipDialog;
+    }
+
+    @Override
+    public void onConfirmationDialogResult(
+            @DialogType int dialogType, @ActionConfirmationResult int result) {
+        if (mInnerListener != null) {
+            mInnerListener.onConfirmationDialogResult(dialogType, result);
+        }
+        switch (dialogType) {
+            case DialogType.SYNC:
+                handleSyncDialogResult(result);
+                break;
+            case DialogType.COLLABORATION:
+                break; // Intentional no-op work was already done.
+            case DialogType.NONE:
+                mDoPinRunnable.run();
+                break;
+            default:
+                assert false : "Unexpected dialog type: " + dialogType;
+        }
+    }
+
+    private void handleSyncDialogResult(@ActionConfirmationResult int result) {
+        switch (result) {
+            case ActionConfirmationResult.CONFIRMATION_NEGATIVE:
+                // Action was cancelled.
+                break;
+            case ActionConfirmationResult.CONFIRMATION_POSITIVE: // Fallthrough.
+            case ActionConfirmationResult.IMMEDIATE_CONTINUE:
+                mDoPinRunnable.run();
+                break;
+            default:
+                assert false : "Unexpected result: " + result;
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPinnerActionListenerUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPinnerActionListenerUnitTest.java
new file mode 100644
index 0000000..bc9aa24
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPinnerActionListenerUnitTest.java
@@ -0,0 +1,124 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tabmodel;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.tabmodel.TabModelActionListener.DialogType;
+import org.chromium.components.browser_ui.widget.ActionConfirmationResult;
+
+/** Unit tests for {@link TabPinnerActionListener}. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class TabPinnerActionListenerUnitTest {
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock private Runnable mRunnable;
+    @Mock private TabModelActionListener mInnerListener;
+
+    private TabPinnerActionListener mListener;
+
+    @Before
+    public void setUp() {
+        mListener = new TabPinnerActionListener(mRunnable, mInnerListener);
+    }
+
+    @Test
+    public void testNoDialog() {
+        mListener.willPerformActionOrShowDialog(DialogType.NONE, true);
+        verify(mInnerListener).willPerformActionOrShowDialog(DialogType.NONE, true);
+        mListener.pinIfCollaborationDialogShown();
+        verify(mRunnable, never()).run();
+
+        mListener.onConfirmationDialogResult(
+                DialogType.NONE, ActionConfirmationResult.IMMEDIATE_CONTINUE);
+        verify(mInnerListener)
+                .onConfirmationDialogResult(
+                        DialogType.NONE, ActionConfirmationResult.IMMEDIATE_CONTINUE);
+        verify(mRunnable).run();
+    }
+
+    @Test
+    public void testOnConfirmationDialogResult_Sync_Positive() {
+        mListener.willPerformActionOrShowDialog(DialogType.SYNC, false);
+        verify(mInnerListener).willPerformActionOrShowDialog(DialogType.SYNC, false);
+        mListener.pinIfCollaborationDialogShown();
+        verify(mRunnable, never()).run();
+
+        mListener.onConfirmationDialogResult(
+                DialogType.SYNC, ActionConfirmationResult.CONFIRMATION_POSITIVE);
+        verify(mInnerListener)
+                .onConfirmationDialogResult(
+                        DialogType.SYNC, ActionConfirmationResult.CONFIRMATION_POSITIVE);
+        verify(mRunnable).run();
+    }
+
+    @Test
+    public void testOnConfirmationDialogResult_Sync_Immediate() {
+        mListener.willPerformActionOrShowDialog(DialogType.SYNC, false);
+        verify(mInnerListener).willPerformActionOrShowDialog(DialogType.SYNC, false);
+        mListener.pinIfCollaborationDialogShown();
+        verify(mRunnable, never()).run();
+
+        mListener.onConfirmationDialogResult(
+                DialogType.SYNC, ActionConfirmationResult.IMMEDIATE_CONTINUE);
+        verify(mInnerListener)
+                .onConfirmationDialogResult(
+                        DialogType.SYNC, ActionConfirmationResult.IMMEDIATE_CONTINUE);
+        verify(mRunnable).run();
+    }
+
+    @Test
+    public void testOnConfirmationDialogResult_Sync_Negative() {
+        mListener.willPerformActionOrShowDialog(DialogType.SYNC, false);
+        verify(mInnerListener).willPerformActionOrShowDialog(DialogType.SYNC, false);
+        mListener.pinIfCollaborationDialogShown();
+        verify(mRunnable, never()).run();
+
+        mListener.onConfirmationDialogResult(
+                DialogType.SYNC, ActionConfirmationResult.CONFIRMATION_NEGATIVE);
+        verify(mInnerListener)
+                .onConfirmationDialogResult(
+                        DialogType.SYNC, ActionConfirmationResult.CONFIRMATION_NEGATIVE);
+        verify(mRunnable, never()).run();
+    }
+
+    @Test
+    public void testCollaborationFlow() {
+        mListener.willPerformActionOrShowDialog(DialogType.COLLABORATION, false);
+        verify(mInnerListener).willPerformActionOrShowDialog(DialogType.COLLABORATION, false);
+        verify(mRunnable, never()).run();
+
+        mListener.pinIfCollaborationDialogShown();
+        verify(mRunnable).run();
+
+        mListener.onConfirmationDialogResult(
+                DialogType.COLLABORATION, ActionConfirmationResult.CONFIRMATION_POSITIVE);
+        verify(mInnerListener)
+                .onConfirmationDialogResult(
+                        DialogType.COLLABORATION, ActionConfirmationResult.CONFIRMATION_POSITIVE);
+
+        // Still only run once.
+        verify(mRunnable).run();
+    }
+
+    @Test
+    public void testNoInnerListener_ImmediateContinue() {
+        mListener = new TabPinnerActionListener(mRunnable, null);
+        mListener.willPerformActionOrShowDialog(DialogType.NONE, true);
+        mListener.onConfirmationDialogResult(
+                DialogType.NONE, ActionConfirmationResult.IMMEDIATE_CONTINUE);
+        verify(mRunnable).run();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 0fff616..601c8597 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -38,6 +38,7 @@
 import org.chromium.base.supplier.OneShotCallback;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.supplier.OneshotSupplierImpl;
+import org.chromium.base.supplier.TransitiveObservableSupplier;
 import org.chromium.base.supplier.UnownedUserDataSupplier;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ActivityTabProvider;
@@ -144,6 +145,7 @@
 import org.chromium.chrome.browser.toolbar.menu_button.MenuButtonCoordinator;
 import org.chromium.chrome.browser.toolbar.top.ToolbarActionModeCallback;
 import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer;
+import org.chromium.chrome.browser.toolbar.top.tab_strip.StripVisibilityState;
 import org.chromium.chrome.browser.ui.activity_recreation.ActivityRecreationController;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuBlocker;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinator;
@@ -313,6 +315,8 @@
     private @Nullable ScrollCaptureManager mScrollCaptureManager;
     protected final ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
     protected final ObservableSupplier<LayoutManagerImpl> mLayoutManagerImplSupplier;
+    protected final TransitiveObservableSupplier<LayoutManagerImpl, @StripVisibilityState Integer>
+            mTabStripVisibilitySupplier;
     protected final ObservableSupplierImpl<LayoutManager> mLayoutManagerSupplier;
     protected final ObservableSupplier<ModalDialogManager> mModalDialogManagerSupplier;
     private final AppMenuBlocker mAppMenuBlocker;
@@ -501,6 +505,13 @@
                 };
         mLayoutManagerImplSupplier = layoutManagerSupplier;
         mLayoutManagerImplSupplier.addObserver(mLayoutManagerSupplierCallback);
+        mTabStripVisibilitySupplier =
+                new TransitiveObservableSupplier<>(
+                        mLayoutManagerImplSupplier,
+                        (layoutManagerImpl) ->
+                                layoutManagerImpl
+                                        .getStripLayoutHelperManager()
+                                        .getStripVisibilityStateSupplier());
 
         mShareDelegateSupplier = shareDelegateSupplier;
         mTabObscuringHandlerSupplier.set(new TabObscuringHandler());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java
index 760d052..66aad18 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java
@@ -39,6 +39,7 @@
 import android.widget.EditText;
 import android.widget.TextView;
 
+import androidx.annotation.LayoutRes;
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AlertDialog;
 import androidx.test.espresso.intent.Intents;
@@ -66,6 +67,7 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Features;
 import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.autofill.AndroidAutofillAvailabilityStatus;
 import org.chromium.chrome.browser.autofill.AutofillClientProviderUtils;
 import org.chromium.chrome.browser.autofill.AutofillTestHelper;
@@ -318,111 +320,72 @@
                 plusAddressEntry.getSummary());
     }
 
+    /**
+     * Verifies that clicking the edit link for specific profile types launches an external URL.
+     *
+     * <p>This helper is for testing profiles that are not editable within the app, such as Home and
+     * Work addresses, which are managed in the user's Google Account. It checks that the preference
+     * displays the correct icon and that clicking the "Edit" link correctly fires an ACTION_VIEW
+     * intent for the specified URL.
+     *
+     * @param profile The profile to add and test.
+     * @param expectedWidgetLayout The expected resource ID for the preference's widget layout.
+     * @param expectedUrl The external URL that is expected to be launched for editing.
+     */
+    private void testExternalEditEntryPoint(
+            AutofillProfile profile, @LayoutRes int expectedWidgetLayout, String expectedUrl)
+            throws Exception {
+        mHelper.setProfile(profile);
+        AutofillProfilesFragment fragment = sSettingsActivityTestRule.getFragment();
+
+        AutofillProfileEditorPreference profilePreference =
+                fragment.findPreference(profile.getInfo(FieldType.NAME_FULL));
+        assertNotNull(profilePreference);
+        assertEquals(expectedWidgetLayout, profilePreference.getWidgetLayoutResource());
+
+        // Edit a profile.
+        ThreadUtils.runOnUiThreadBlocking(profilePreference::performClick);
+
+        // Mock the intent that should be fired.
+        Instrumentation.ActivityResult result =
+                new Instrumentation.ActivityResult(Activity.RESULT_OK, null);
+        var intentMatcher = allOf(hasAction(Intent.ACTION_VIEW), hasData(Uri.parse(expectedUrl)));
+        intending(intentMatcher).respondWith(result);
+
+        // Click the "Edit" link and verify the correct intent was sent.
+        Context context = fragment.getContext();
+        onView(withText(context.getString(R.string.autofill_edit_address_label))).perform(click());
+        intended(intentMatcher);
+    }
+
     @Test
     @MediumTest
     @Feature({"Preferences"})
     public void testHomeEntry() throws Exception {
-        mHelper.setProfile(sHomeProfile);
-        AutofillProfilesFragment autofillProfileFragment = sSettingsActivityTestRule.getFragment();
-
-        AutofillProfileEditorPreference homeProfilePreference =
-                autofillProfileFragment.findPreference(sHomeProfile.getInfo(FieldType.NAME_FULL));
-        assertNotNull(homeProfilePreference);
-        assertEquals(
+        testExternalEditEntryPoint(
+                sHomeProfile,
                 R.layout.autofill_settings_home_profile_icon,
-                homeProfilePreference.getWidgetLayoutResource());
-
-        // Edit a profile.
-        ThreadUtils.runOnUiThreadBlocking(homeProfilePreference::performClick);
-
-        // Define a fake result to return immediately when the intent is caught.
-        // This prevents the actual urls from being launched.
-        Instrumentation.ActivityResult ok_result =
-                new Instrumentation.ActivityResult(Activity.RESULT_OK, null);
-        var homeIntentMatcher =
-                allOf(
-                        hasAction(Intent.ACTION_VIEW),
-                        hasData(
-                                Uri.parse(
-                                        AutofillProfilesFragment
-                                                .GOOGLE_ACCOUNT_HOME_ADDRESS_EDIT_URL)));
-        intending(homeIntentMatcher).respondWith(ok_result);
-
-        // Try to find a view with "Link" and click on it.
-        Context context = autofillProfileFragment.getContext();
-        onView(withText(context.getString(R.string.autofill_edit_address_label))).perform(click());
-        intended(homeIntentMatcher);
+                AutofillProfilesFragment.GOOGLE_ACCOUNT_HOME_ADDRESS_EDIT_URL);
     }
 
     @Test
     @MediumTest
     @Feature({"Preferences"})
     public void testWorkEntry() throws Exception {
-        mHelper.setProfile(sWorkProfile);
-        AutofillProfilesFragment autofillProfileFragment = sSettingsActivityTestRule.getFragment();
-
-        AutofillProfileEditorPreference workProfilePreference =
-                autofillProfileFragment.findPreference(sWorkProfile.getInfo(FieldType.NAME_FULL));
-        assertNotNull(workProfilePreference);
-        assertEquals(
+        testExternalEditEntryPoint(
+                sWorkProfile,
                 R.layout.autofill_settings_work_profile_icon,
-                workProfilePreference.getWidgetLayoutResource());
-
-        // Edit a profile.
-        ThreadUtils.runOnUiThreadBlocking(workProfilePreference::performClick);
-
-        // Define a fake result to return immediately when the intent is caught.
-        // This prevents the actual urls from being launched.
-        Instrumentation.ActivityResult ok_result =
-                new Instrumentation.ActivityResult(Activity.RESULT_OK, null);
-        var workIntentMatcher =
-                allOf(
-                        hasAction(Intent.ACTION_VIEW),
-                        hasData(
-                                Uri.parse(
-                                        AutofillProfilesFragment
-                                                .GOOGLE_ACCOUNT_WORK_ADDRESS_EDIT_URL)));
-        intending(workIntentMatcher).respondWith(ok_result);
-
-        // Try to find a view with "Link" and click on it.
-        Context context = autofillProfileFragment.getContext();
-        onView(withText(context.getString(R.string.autofill_edit_address_label))).perform(click());
-        intended(workIntentMatcher);
+                AutofillProfilesFragment.GOOGLE_ACCOUNT_WORK_ADDRESS_EDIT_URL);
     }
 
     @Test
     @MediumTest
     @Feature({"Preferences"})
     public void testAccountNameEmailEntry() throws Exception {
-        mHelper.setProfile(sAccountNameEmailProfile);
-        AutofillProfilesFragment autofillProfileFragment = sSettingsActivityTestRule.getFragment();
-
-        AutofillProfileEditorPreference accountNameEmailPreference =
-                autofillProfileFragment.findPreference(
-                        sAccountNameEmailProfile.getInfo(FieldType.NAME_FULL));
-        assertNotNull(accountNameEmailPreference);
-        assertEquals(0, accountNameEmailPreference.getWidgetLayoutResource());
-
-        // Edit a profile.
-        ThreadUtils.runOnUiThreadBlocking(accountNameEmailPreference::performClick);
-
-        // Define a fake result to return immediately when the intent is caught.
-        // This prevents the actual urls from being launched.
-        Instrumentation.ActivityResult ok_result =
-                new Instrumentation.ActivityResult(Activity.RESULT_OK, null);
-        var nameEmailIntentMatcher =
-                allOf(
-                        hasAction(Intent.ACTION_VIEW),
-                        hasData(
-                                Uri.parse(
-                                        AutofillProfilesFragment
-                                                .GOOGLE_ACCOUNT_NAME_EMAIL_ADDRESS_EDIT_URL)));
-        intending(nameEmailIntentMatcher).respondWith(ok_result);
-
-        // Try to find a view with "Link" and click on it.
-        Context context = autofillProfileFragment.getContext();
-        onView(withText(context.getString(R.string.autofill_edit_address_label))).perform(click());
-        intended(nameEmailIntentMatcher);
+        testExternalEditEntryPoint(
+                sAccountNameEmailProfile,
+                0, // No widget layout resource
+                AutofillProfilesFragment.GOOGLE_ACCOUNT_NAME_EMAIL_ADDRESS_EDIT_URL);
     }
 
     @Test
@@ -474,6 +437,85 @@
                 true);
     }
 
+    /**
+     * A helper method for testing the deletion of an Autofill profile. Clicks the deletion button
+     * but cancels the flow. Clicks the deletion button again and confirms the deletion dialog by
+     * clicking the corresponding positive button.
+     *
+     * @param profileNameToDelete The name of the profile to be deleted.
+     * @param initialCount The number of preferences expected before the deletion.
+     * @param expectedConfirmationMessage The confirmation message expected to be shown to the user.
+     */
+    private void testDeleteProfile(
+            String profileNameToDelete, int initialCount, String expectedConfirmationMessage)
+            throws Exception {
+        AutofillProfilesFragment autofillProfileFragment = sSettingsActivityTestRule.getFragment();
+
+        // Check the preferences on the initial screen.
+        checkPreferenceCount(initialCount);
+        AutofillProfileEditorPreference profileToDelete =
+                autofillProfileFragment.findPreference(profileNameToDelete);
+        assertNotNull("Profile to delete not found", profileToDelete);
+        assertEquals(profileNameToDelete, profileToDelete.getTitle());
+
+        // --- Part 1: Attempt to delete the profile, but cancel on confirmation. ---
+        HistogramWatcher deletionCanceledHistogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectBooleanRecordTimes(
+                                EditorDialogView.PROFILE_DELETED_HISTOGRAM,
+                                /* value= */ false,
+                                /* times= */ 1)
+                        .expectBooleanRecordTimes(
+                                EditorDialogView.PROFILE_DELETED_SETTINGS_HISTOGRAM,
+                                /* value= */ false,
+                                /* times= */ 1)
+                        .build();
+        ThreadUtils.runOnUiThreadBlocking(profileToDelete::performClick);
+        EditorDialogView editorDialog = autofillProfileFragment.getEditorDialogForTest();
+        rule.setEditorDialogAndWait(editorDialog);
+        rule.clickInEditorAndWaitForConfirmationDialog(R.id.delete_menu_id);
+
+        // Verify the confirmation message is correct.
+        AlertDialog confirmationDialog = editorDialog.getConfirmationDialogForTest();
+        assertNotNull(confirmationDialog);
+        TextView messageView = confirmationDialog.findViewById(R.id.confirmation_dialog_message);
+        assertEquals(expectedConfirmationMessage, messageView.getText().toString());
+
+        // Click cancel and ensure we return to the editor, then the main list.
+        rule.clickInConfirmationDialogAndWait(
+                DialogInterface.BUTTON_NEGATIVE, /* waitForPreferenceUpdate= */ false);
+        rule.clickInEditorAndWait(
+                R.id.payments_edit_cancel_button, /* waitForPreferenceUpdate= */ false);
+
+        // Verify that the profile was NOT deleted.
+        checkPreferenceCount(initialCount);
+        assertNotNull(findPreference(profileNameToDelete));
+        deletionCanceledHistogramWatcher.assertExpected();
+
+        // --- Part 2: Delete the profile and confirm it. ---
+        HistogramWatcher deletionConfirmedHistogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectBooleanRecordTimes(
+                                EditorDialogView.PROFILE_DELETED_HISTOGRAM,
+                                /* value= */ true,
+                                /* times= */ 1)
+                        .expectBooleanRecordTimes(
+                                EditorDialogView.PROFILE_DELETED_SETTINGS_HISTOGRAM,
+                                /* value= */ true,
+                                /* times= */ 1)
+                        .build();
+        ThreadUtils.runOnUiThreadBlocking(profileToDelete::performClick);
+        rule.setEditorDialogAndWait(autofillProfileFragment.getEditorDialogForTest());
+        rule.clickInEditorAndWaitForConfirmationDialog(R.id.delete_menu_id);
+        rule.clickInConfirmationDialogAndWait(
+                DialogInterface.BUTTON_POSITIVE, /* waitForPreferenceUpdate= */ true);
+
+        // Verify that the profile IS deleted and the preference count has decreased.
+        checkPreferenceCount(initialCount - 1);
+        assertNull("Profile should have been deleted", findPreference(profileNameToDelete));
+        deletionConfirmedHistogramWatcher.assertExpected();
+    }
+
     @Test
     @MediumTest
     @Feature({"Preferences"})
@@ -481,6 +523,8 @@
         Context context = sSettingsActivityTestRule.getFragment().getContext();
         setUpMockSyncService(false, new HashSet());
         testDeleteProfile(
+                "Seb Doe",
+                7 /* toggle + add button + 4 profiles + plus address entry */,
                 context.getString(R.string.autofill_delete_local_address_record_type_notice));
     }
 
@@ -491,95 +535,30 @@
         Context context = sSettingsActivityTestRule.getFragment().getContext();
         setUpMockSyncService(true, Collections.singleton(UserSelectableType.AUTOFILL));
         testDeleteProfile(
+                "Seb Doe",
+                7 /* toggle + add button + 4 profiles + plus address entry */,
                 context.getString(R.string.autofill_delete_sync_address_record_type_notice));
     }
 
-    public void testDeleteProfile(String expectedConfirmationMessage) throws Exception {
-        AutofillProfilesFragment autofillProfileFragment = sSettingsActivityTestRule.getFragment();
-
-        // Check the preferences on the initial screen.
-        checkPreferenceCount(
-                7 /* One toggle + one add button + four profiles + plus address entry. */);
-        AutofillProfileEditorPreference sebProfile =
-                autofillProfileFragment.findPreference("Seb Doe");
-        assertNotNull(sebProfile);
-        assertEquals("Seb Doe", sebProfile.getTitle());
-
-        // Delete the profile, but cancel on confirmation.
-        ThreadUtils.runOnUiThreadBlocking(sebProfile::performClick);
-        EditorDialogView editorDialog = autofillProfileFragment.getEditorDialogForTest();
-        rule.setEditorDialogAndWait(editorDialog);
-        rule.clickInEditorAndWaitForConfirmationDialog(R.id.delete_menu_id);
-
-        // Verify the confirmation message for non-account profile.
-        AlertDialog confirmationDialog = editorDialog.getConfirmationDialogForTest();
-        assertNotNull(confirmationDialog);
-        TextView messageView = confirmationDialog.findViewById(R.id.confirmation_dialog_message);
-        assertEquals(expectedConfirmationMessage.toString(), messageView.getText().toString());
-
-        // Get back to the profile list.
-        rule.clickInConfirmationDialogAndWait(
-                DialogInterface.BUTTON_NEGATIVE, /* waitForPreferenceUpdate= */ false);
-        rule.clickInEditorAndWait(
-                R.id.payments_edit_cancel_button, /* waitForPreferenceUpdate= */ false);
-
-        // Make sure the profile is not deleted and the number of profiles didn't change.
-        checkPreferenceCount(
-                7 /* One toggle + one add button + four profiles + plus address entry. */);
-
-        // Delete a profile and confirm it.
-        ThreadUtils.runOnUiThreadBlocking(sebProfile::performClick);
-        rule.setEditorDialogAndWait(autofillProfileFragment.getEditorDialogForTest());
-        rule.clickInEditorAndWaitForConfirmationDialog(R.id.delete_menu_id);
-        rule.clickInConfirmationDialogAndWait(
-                DialogInterface.BUTTON_POSITIVE, /* waitForPreferenceUpdate= */ true);
-
-        // Make sure the profile is deleted.
-        checkPreferenceCount(
-                6 /* One toggle + one add button + three profiles + plus address entry. */);
-        assertNotNull(findPreference("John Doe"));
-        assertNull(findPreference("Seb Doe"));
-    }
-
     @Test
     @MediumTest
     @Feature({"Preferences"})
     public void testDeleteAccountProfile() throws Exception {
+        // Setup specific to this test case.
         setUpMockPrimaryAccount(TestAccounts.ACCOUNT1);
-
-        AutofillProfilesFragment autofillProfileFragment = sSettingsActivityTestRule.getFragment();
-        Context context = autofillProfileFragment.getContext();
-
         mHelper.setProfile(sAccountProfile);
+        Context context = sSettingsActivityTestRule.getFragment().getContext();
 
-        // Check the preferences on the initial screen.
-        checkPreferenceCount(
-                8 /* One toggle + one add button + five profiles + plus address entry. */);
-        AutofillProfileEditorPreference artikProfile = findPreference("Artik Doe");
-        assertNotNull(artikProfile);
-
-        // Delete Artik's account profile.
-        ThreadUtils.runOnUiThreadBlocking(artikProfile::performClick);
-        EditorDialogView editorDialog = autofillProfileFragment.getEditorDialogForTest();
-        rule.setEditorDialogAndWait(editorDialog);
-        rule.clickInEditorAndWaitForConfirmationDialog(R.id.delete_menu_id);
-
-        // Verify the message.
-        AlertDialog confirmationDialog = editorDialog.getConfirmationDialogForTest();
-        assertNotNull(confirmationDialog);
-        TextView messageView = confirmationDialog.findViewById(R.id.confirmation_dialog_message);
+        // Prepare the expected confirmation message with the account email.
         String expectedMessage =
                 context.getString(R.string.autofill_delete_account_address_record_type_notice)
                         .replace("$1", TestAccounts.ACCOUNT1.getEmail());
-        assertEquals(expectedMessage.toString(), messageView.getText().toString());
 
-        rule.clickInConfirmationDialogAndWait(
-                DialogInterface.BUTTON_POSITIVE, /* waitForPreferenceUpdate= */ true);
-
-        // Make sure the profile is deleted.
-        checkPreferenceCount(
-                7 /* One toggle + one add button + four profiles + plus address entry. */);
-        assertNull(findPreference("Artik Doe"));
+        // Call the reusable helper method with parameters for this test.
+        testDeleteProfile(
+                "Artik Doe",
+                8 /* toggle + add button + 5 profiles + plus address entry */,
+                expectedMessage);
     }
 
     @Test
@@ -848,6 +827,29 @@
                 R.id.payments_edit_cancel_button, /* waitForPreferenceUpdate= */ false);
     }
 
+    /**
+     * Verifies that profile icons are shown correctly for the currently established sign-in and
+     * sync state.
+     *
+     * @param expectedLocalIconLayout The expected widget layout for the local/sync profile.
+     */
+    private void verifyAddressProfileIcons(@LayoutRes int expectedLocalIconLayout)
+            throws Exception {
+        // Trigger the address profile list to be rebuilt with the new state.
+        mHelper.setProfile(sAccountProfile);
+
+        // The account profile icon is always hidden.
+        AutofillProfileEditorPreference accountProfilePreference =
+                findPreference(sAccountProfile.getInfo(FieldType.NAME_FULL));
+        assertEquals(0, accountProfilePreference.getWidgetLayoutResource());
+
+        // The local/sync profile icon visibility depends on the sync state.
+        AutofillProfileEditorPreference localOrSyncProfilePreference =
+                findPreference(sLocalOrSyncProfile.getInfo(FieldType.NAME_FULL));
+        assertEquals(
+                expectedLocalIconLayout, localOrSyncProfilePreference.getWidgetLayoutResource());
+    }
+
     @Test
     @MediumTest
     @Feature({"Preferences"})
@@ -856,17 +858,9 @@
         when(IdentityServicesProvider.get().getIdentityManager(any()))
                 .thenReturn(mIdentityManagerMock);
         when(mIdentityManagerMock.hasPrimaryAccount(ConsentLevel.SIGNIN)).thenReturn(false);
-        setUpMockSyncService(false, new HashSet());
+        setUpMockSyncService(false, new HashSet<>());
 
-        // Trigger address profile list rebuild.
-        mHelper.setProfile(sAccountProfile);
-        AutofillProfileEditorPreference accountProfilePreference =
-                findPreference(sAccountProfile.getInfo(FieldType.NAME_FULL));
-        assertEquals(0, accountProfilePreference.getWidgetLayoutResource());
-
-        AutofillProfileEditorPreference localOrSyncProfilePreference =
-                findPreference(sLocalOrSyncProfile.getInfo(FieldType.NAME_FULL));
-        assertEquals(0, localOrSyncProfilePreference.getWidgetLayoutResource());
+        verifyAddressProfileIcons(/* expectedLocalIconLayout= */ 0);
     }
 
     @Test
@@ -874,39 +868,19 @@
     @Feature({"Preferences"})
     public void testLocalProfiles_NoSync() throws Exception {
         setUpMockPrimaryAccount(TestAccounts.ACCOUNT1);
-        setUpMockSyncService(false, new HashSet());
+        setUpMockSyncService(false, new HashSet<>());
 
-        // Trigger address profile list rebuild.
-        mHelper.setProfile(sAccountProfile);
-        AutofillProfileEditorPreference accountProfilePreference =
-                findPreference(sAccountProfile.getInfo(FieldType.NAME_FULL));
-        assertEquals(0, accountProfilePreference.getWidgetLayoutResource());
-
-        AutofillProfileEditorPreference localOrSyncProfilePreference =
-                findPreference(sLocalOrSyncProfile.getInfo(FieldType.NAME_FULL));
-        assertEquals(
-                R.layout.autofill_settings_local_profile_icon,
-                localOrSyncProfilePreference.getWidgetLayoutResource());
+        verifyAddressProfileIcons(R.layout.autofill_settings_local_profile_icon);
     }
 
     @Test
     @MediumTest
     @Feature({"Preferences"})
-    public void testDisplayedProfileIcons__AddressesNotSynced() throws Exception {
+    public void testDisplayedProfileIcons_AddressesNotSynced() throws Exception {
         setUpMockPrimaryAccount(TestAccounts.ACCOUNT1);
-        setUpMockSyncService(true, new HashSet());
+        setUpMockSyncService(true, new HashSet<>());
 
-        // Trigger address profile list rebuild.
-        mHelper.setProfile(sAccountProfile);
-        AutofillProfileEditorPreference accountProfilePreference =
-                findPreference(sAccountProfile.getInfo(FieldType.NAME_FULL));
-        assertEquals(0, accountProfilePreference.getWidgetLayoutResource());
-
-        AutofillProfileEditorPreference localOrSyncProfilePreference =
-                findPreference(sLocalOrSyncProfile.getInfo(FieldType.NAME_FULL));
-        assertEquals(
-                R.layout.autofill_settings_local_profile_icon,
-                localOrSyncProfilePreference.getWidgetLayoutResource());
+        verifyAddressProfileIcons(R.layout.autofill_settings_local_profile_icon);
     }
 
     @Test
@@ -916,15 +890,7 @@
         setUpMockPrimaryAccount(TestAccounts.ACCOUNT1);
         setUpMockSyncService(true, Collections.singleton(UserSelectableType.AUTOFILL));
 
-        // Trigger address profile list rebuild.
-        mHelper.setProfile(sAccountProfile);
-        AutofillProfileEditorPreference accountProfilePreference =
-                findPreference(sAccountProfile.getInfo(FieldType.NAME_FULL));
-        assertEquals(0, accountProfilePreference.getWidgetLayoutResource());
-
-        AutofillProfileEditorPreference localOrSyncProfilePreference =
-                findPreference(sLocalOrSyncProfile.getInfo(FieldType.NAME_FULL));
-        assertEquals(0, localOrSyncProfilePreference.getWidgetLayoutResource());
+        verifyAddressProfileIcons(/* expectedLocalIconLayout= */ 0);
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripPinUnpinTabsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripPinUnpinTabsTest.java
index 3688a076..4ea9e1ff4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripPinUnpinTabsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripPinUnpinTabsTest.java
@@ -14,6 +14,7 @@
 import static org.hamcrest.CoreMatchers.allOf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;
@@ -48,7 +49,10 @@
 /** Instrumentation tests for pinning and unpinning tabs on LFF tab strip */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @Batch(Batch.PER_CLASS)
-@EnableFeatures(ChromeFeatureList.ANDROID_PINNED_TABS_TABLET_TAB_STRIP)
+@EnableFeatures({
+    ChromeFeatureList.ANDROID_PINNED_TABS_TABLET_TAB_STRIP,
+    ChromeFeatureList.ANDROID_PINNED_TABS
+})
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @Restriction(DeviceFormFactor.TABLET_OR_DESKTOP)
 public class TabStripPinUnpinTabsTest {
@@ -162,27 +166,42 @@
         mStripLayoutHelper =
                 TabStripTestUtils.getActiveStripLayoutHelper(mActivityTestRule.getActivity());
         StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
+        StripLayoutTab tabToPin = tabs[firstGroupedIndex];
+        TabGroupModelFilter groupModelFilter =
+                TabStripTestUtils.getTabGroupModelFilter(
+                        mActivityTestRule.getActivity(), /* isIncognito= */ false);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    var tab = mTabModel.getTabById(tabToPin.getTabId());
+                    assertEquals(firstGroupedIndex, groupModelFilter.getTabModel().indexOf(tab));
+                    assertFalse(tabToPin.getIsPinned());
+                    assertNotNull(tab.getTabGroupId());
+                });
 
         // Open menu and pin tab.
-        StripLayoutTab tabToPin = tabs[firstGroupedIndex];
         showMenu(firstGroupedIndex);
         assertFalse("Tab should not be pinned.", tabToPin.getIsPinned());
         onView(withText(mPinTabMenuLabel)).check(matches(isDisplayed()));
         onView(withText(mPinTabMenuLabel)).perform(click());
 
         // Verify the tab being pinned is ungrouped.
-        TabGroupModelFilter groupModelFilter =
-                TabStripTestUtils.getTabGroupModelFilter(
-                        mActivityTestRule.getActivity(), /* isIncognito= */ false);
         ThreadUtils.runOnUiThreadBlocking(
-                () ->
-                        assertFalse(
-                                "Tab should be ungrouped.",
-                                groupModelFilter.isTabInTabGroup(
-                                        mTabModel.getTabAt(firstGroupedIndex))));
+                () -> {
+                    assertFalse(
+                            "Tab should be ungrouped.",
+                            groupModelFilter.isTabInTabGroup(
+                                    mTabModel.getTabAt(firstGroupedIndex)));
+                    var tab = mTabModel.getTabById(tabToPin.getTabId());
+                    assertEquals(0, groupModelFilter.getTabModel().indexOf(tab));
+                    assertTrue(tabToPin.getIsPinned());
+                });
 
         // Verify the tab is pinned, moved to the front and has correct width.
-        verifyTabIsPinned(tabs, tabToPin, /* expectedDrawX= */ 0f, /* expectedIndex= */ 0);
+        verifyTabIsPinned(
+                mStripLayoutHelper.getStripLayoutTabsForTesting(),
+                tabToPin,
+                /* expectedDrawX= */ 0f,
+                /* expectedIndex= */ 0);
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/desktop_windowing/AppHeaderCoordinatorBrowserTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/desktop_windowing/AppHeaderCoordinatorBrowserTest.java
index caffd50..a936811 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/desktop_windowing/AppHeaderCoordinatorBrowserTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/desktop_windowing/AppHeaderCoordinatorBrowserTest.java
@@ -164,7 +164,6 @@
 
     @Test
     @MediumTest
-    @EnableFeatures(ChromeFeatureList.TAB_STRIP_TRANSITION_IN_DESKTOP_WINDOW)
     public void testToggleTabStripVisibilityInDesktopWindow() {
         ChromeTabbedActivity activity = mActivityTestRule.getActivity();
         triggerDesktopWindowingModeChange(activity, true);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/ScreenshotCaptureTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/ScreenshotCaptureTest.java
index a53f369..6789a28 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/ScreenshotCaptureTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/ScreenshotCaptureTest.java
@@ -38,7 +38,6 @@
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenManagerTestUtils;
@@ -148,25 +147,7 @@
     @MediumTest
     @Feature({"RenderTest"})
     @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
-    public void testNavigatingAwayFromNtpToNormalPageSoftware(boolean nightModeEnabled)
-            throws IOException, TimeoutException, InterruptedException {
-        mRenderTestRule.setVariantPrefix("software");
-        navigatingAwayFromNtpToNormalPage();
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"RenderTest"})
-    @EnableFeatures({ChromeFeatureList.NATIVE_PAGE_TRANSITION_HARDWARE_CAPTURE})
-    @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
-    @DisableIf.Build(sdk_is_less_than = VERSION_CODES.S)
-    public void testNavigatingAwayFromNtpToNormalPageHardware(boolean nightModeEnabled)
-            throws IOException, TimeoutException, InterruptedException {
-        mRenderTestRule.setVariantPrefix("hardware");
-        navigatingAwayFromNtpToNormalPage();
-    }
-
-    private void navigatingAwayFromNtpToNormalPage()
+    public void testNavigatingAwayFromNtpToNormalPage(boolean nightModeEnabled)
             throws IOException, TimeoutException, InterruptedException {
         mActivityTestRule.startOnNtp();
         UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
index 5c050d7..5ade476e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
@@ -96,6 +96,18 @@
                     "SafeBrowsing.SuspiciousNotificationWarning."
                             + "ShowOriginalNotifications.SuspiciousNotificationsDroppedCount";
 
+    private static final String SAFE_BROWSING_NOTIFICATION_REVOCATION_SOURCE_HISTOGRAM_NAME =
+            "SafeBrowsing.NotificationRevocationSource";
+    // These represent the values logged in the
+    // `SAFE_BROWSING_NOTIFICATION_REVOCATION_SOURCE_HISTOGRAM_NAME` histogram. The values are
+    // defined in the safe_browsing::NotificationRevocationSource enum class.
+    // Enum value corresponding to a revocation happening when a user unsubscribes on a notification
+    // where a suspicious content warning was NOT shown.
+    private static final int STANDARD_ONE_TAP_UNSUBSCRIBE_EVENT = 2;
+    // Enum value corresponding to a revocation happening when a user unsubscribes on a notification
+    // where a suspicious content warning was shown.
+    private static final int SUSPICIOUS_WARNING_ONE_TAP_UNSUBSCRIBE_EVENT = 3;
+
     @Before
     public void setUp() {
         SiteEngagementService.setParamValuesForTesting();
@@ -750,6 +762,13 @@
     @Feature({"Browser", "Notifications"})
     @Features.EnableFeatures(ChromeFeatureList.NOTIFICATION_ONE_TAP_UNSUBSCRIBE)
     public void testNotificationProvisionalUnsubscribeAndCommit() throws Exception {
+        var histogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(
+                                SAFE_BROWSING_NOTIFICATION_REVOCATION_SOURCE_HISTOGRAM_NAME,
+                                STANDARD_ONE_TAP_UNSUBSCRIBE_EVENT)
+                        .build();
+
         mNotificationTestRule.setNotificationContentSettingForOrigin(
                 ContentSetting.ALLOW, mPermissionTestRule.getOrigin());
         Assert.assertEquals("\"granted\"", runJavaScript("Notification.permission"));
@@ -782,6 +801,9 @@
         // This should have caused notifications permission to become reset.
         Assert.assertEquals("\"default\"", runJavaScript("Notification.permission"));
         checkThatShowNotificationIsDenied();
+
+        // Validate histogram is logged correctly.
+        histogramWatcher.assertExpected();
     }
 
     /**
@@ -1581,6 +1603,9 @@
                         .expectIntRecord(
                                 SUSPICIOUS_NOTIFICATION_COUNT_DROPPED_SHOW_ORIGINALS_HISTOGRAM_NAME,
                                 0)
+                        .expectIntRecord(
+                                SAFE_BROWSING_NOTIFICATION_REVOCATION_SOURCE_HISTOGRAM_NAME,
+                                SUSPICIOUS_WARNING_ONE_TAP_UNSUBSCRIBE_EVENT)
                         .build();
 
         mNotificationTestRule.setNotificationContentSettingForOrigin(
@@ -1668,6 +1693,9 @@
                                 SUSPICIOUS_NOTIFICATION_WARNING_INTERACTIONS_HISTOGRAM_NAME,
                                 SuspiciousNotificationWarningInteractions
                                         .REPORT_UNWARNED_NOTIFICATION_AS_SPAM)
+                        .expectIntRecord(
+                                SAFE_BROWSING_NOTIFICATION_REVOCATION_SOURCE_HISTOGRAM_NAME,
+                                STANDARD_ONE_TAP_UNSUBSCRIBE_EVENT)
                         .build();
 
         mNotificationTestRule.setNotificationContentSettingForOrigin(
@@ -1741,6 +1769,9 @@
                                 SUSPICIOUS_NOTIFICATION_WARNING_INTERACTIONS_HISTOGRAM_NAME,
                                 SuspiciousNotificationWarningInteractions.WARNING_SHOWN,
                                 SuspiciousNotificationWarningInteractions.UNSUBSCRIBE)
+                        .expectIntRecord(
+                                SAFE_BROWSING_NOTIFICATION_REVOCATION_SOURCE_HISTOGRAM_NAME,
+                                SUSPICIOUS_WARNING_ONE_TAP_UNSUBSCRIBE_EVENT)
                         .build();
 
         mNotificationTestRule.setNotificationContentSettingForOrigin(
@@ -1838,6 +1869,9 @@
                         .expectIntRecord(
                                 SUSPICIOUS_NOTIFICATION_COUNT_DROPPED_SHOW_ORIGINALS_HISTOGRAM_NAME,
                                 0)
+                        .expectIntRecord(
+                                SAFE_BROWSING_NOTIFICATION_REVOCATION_SOURCE_HISTOGRAM_NAME,
+                                SUSPICIOUS_WARNING_ONE_TAP_UNSUBSCRIBE_EVENT)
                         .build();
 
         mNotificationTestRule.setNotificationContentSettingForOrigin(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java
index 04f6996..c78c1f5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java
@@ -181,7 +181,7 @@
     @DisableIf.Build(
             sdk_is_greater_than = Build.VERSION_CODES.TIRAMISU,
             message = "crbug.com/355058571")
-    public void testOpenRecentlyClosedTab_InCurrentTab() {
+    public void testOpenRecentlyClosedTab_CurrentTabDisposition() {
         final String[] urls = new String[] {getUrl(TEST_PAGE_A), getUrl(TEST_PAGE_B)};
         final Tab tabA = mActivityTestRule.loadUrlInNewTab(urls[0], /* incognito= */ false);
         final Tab tabB = mActivityTestRule.loadUrlInNewTab(urls[1], /* incognito= */ false);
@@ -221,23 +221,24 @@
         // 2. tabC - now TEST_PAGE_B.
         final List<Tab> tabs = getAllTabs();
         Assert.assertEquals(2, tabs.size());
-        // Restored onto tab B.
-        Assert.assertEquals(tabC, tabs.get(1));
-        Assert.assertEquals(titles[1], ChromeTabUtils.getTitleOnUiThread(tabC));
-        Assert.assertEquals(urls[1], ChromeTabUtils.getUrlOnUiThread(tabC).getSpec());
+        // Restored in same position as tabC.
+        Tab newTabC = tabs.get(1);
+        Assert.assertNotEquals(tabC, newTabC);
+        Assert.assertEquals(titles[1], ChromeTabUtils.getTitleOnUiThread(newTabC));
+        Assert.assertEquals(urls[1], ChromeTabUtils.getUrlOnUiThread(newTabC).getSpec());
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
-                    Assert.assertNotNull(tabC.getWebContents());
+                    Assert.assertNotNull(newTabC.getWebContents());
                     // Should only have one navigation entry as it replaced TEST_PAGE_C.
                     Assert.assertEquals(
                             1,
-                            tabC.getWebContents()
+                            newTabC.getWebContents()
                                     .getNavigationController()
                                     .getNavigationHistory()
                                     .getEntryCount());
 
                     // Has renderer for foreground tab.
-                    Assert.assertNotNull(tabC.getWebContents().getRenderWidgetHostView());
+                    Assert.assertNotNull(newTabC.getWebContents().getRenderWidgetHostView());
                 });
     }
 
@@ -410,7 +411,7 @@
     @DisableIf.Build(
             sdk_is_greater_than = Build.VERSION_CODES.TIRAMISU,
             message = "crbug.com/355058571")
-    public void testOpenRecentlyClosedTab_FromGroupClosure_InCurrentTab() {
+    public void testOpenRecentlyClosedTab_FromGroupClosure_CurrentTabDisposition() {
         if (mTabGroupModelFilter == null) return;
 
         // Tab order is inverted in when closing.
@@ -445,7 +446,7 @@
                             mTabModel, group.getTabs().get(1), WindowOpenDisposition.CURRENT_TAB);
                 });
 
-        // 1. tabA restored over blank tab.
+        // 1. tabA state replaced blank tab.
         final List<Tab> tabs = getAllTabs();
         Assert.assertEquals(1, tabs.size());
         Assert.assertEquals(titles[1], ChromeTabUtils.getTitleOnUiThread(tabs.get(0)));
@@ -463,7 +464,7 @@
                             WindowOpenDisposition.NEW_BACKGROUND_TAB);
                 });
 
-        // 1. tabA restored over blank tab.
+        // 1. tabA state replaced blank tab.
         // 2. tabB restored.
         tabs.clear();
         tabs.addAll(getAllTabs());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
index 66f2086..6628294 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
@@ -85,6 +85,8 @@
 import org.chromium.chrome.browser.history.HistoryContentManager;
 import org.chromium.chrome.browser.history.StubbedHistoryProvider;
 import org.chromium.chrome.browser.notifications.channels.SiteChannelsManager;
+import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
+import org.chromium.chrome.browser.pdf.PdfUtils.PdfPageType;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.privacy_sandbox.FakePrivacySandboxBridge;
 import org.chromium.chrome.browser.privacy_sandbox.PrivacySandboxBridgeJni;
@@ -631,7 +633,7 @@
     public void testShowOnInsecureHttpWebsite() throws IOException {
         mTestServerRule.setServerUsesHttps(false);
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
-        onViewWaiting(allOf(withId(R.id.security_description_details), isDisplayed()));
+        onViewWaiting(allOf(withId(R.id.page_info_connection_row), isDisplayed()));
         onView(withText("Connection is not secure")).check(matches(isDisplayed()));
     }
 
@@ -650,7 +652,7 @@
     public void testShowOnExpiredCertificateWebsite() throws IOException {
         mTestServerRule.setCertificateType(ServerCertificate.CERT_EXPIRED);
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
-        onViewWaiting(allOf(withId(R.id.security_description_details), isDisplayed()));
+        onViewWaiting(allOf(withId(R.id.page_info_connection_row), isDisplayed()));
         onView(withText("Connection is not secure")).check(matches(isDisplayed()));
     }
 
@@ -759,6 +761,35 @@
         mRenderTestRule.render(getPageInfoView(), "PageInfo_ConnectionInfoSubpageInsecure");
     }
 
+    /** Tests the connection info page of the PageInfo UI - secure website. */
+    @Test
+    @MediumTest
+    @Feature({"RenderTest"})
+    public void testShowConnectionInfoSubpageSecure() throws IOException {
+        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
+        onView(withId(R.id.page_info_connection_row)).perform(click());
+        onViewWaiting(
+                allOf(
+                        withText(containsString("Test Root CA issued this website's certificate.")),
+                        isDisplayed()));
+        mRenderTestRule.render(getPageInfoView(), "PageInfo_ConnectionInfoSubpageSecure");
+    }
+
+    /** Tests the connection info page of the PageInfo UI - expired certificate. */
+    @Test
+    @MediumTest
+    @Feature({"RenderTest"})
+    public void testShowConnectionInfoSubpageExpiredCert() throws IOException {
+        mTestServerRule.setCertificateType(ServerCertificate.CERT_EXPIRED);
+        loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
+        onView(withId(R.id.page_info_connection_row)).perform(click());
+        onViewWaiting(
+                allOf(
+                        withText(containsString("Server's certificate has expired.")),
+                        isDisplayed()));
+        mRenderTestRule.render(getPageInfoView(), "PageInfo_ConnectionInfoSubpageExpiredCert");
+    }
+
     /** Tests the permissions page of the PageInfo UI with permissions. */
     @Test
     @MediumTest
@@ -1614,6 +1645,153 @@
         }
     }
 
+    /** Tests that page info view is shown correctly for paint preview pages. */
+    @Test
+    @MediumTest
+    public void testPaintPreview() {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    final ChromeActivity activity = mActivityTestRule.getActivity();
+                    final Tab tab = activity.getActivityTab();
+                    ChromePageInfoControllerDelegate pageInfoControllerDelegate =
+                            new ChromePageInfoControllerDelegate(
+                                    activity,
+                                    tab.getWebContents(),
+                                    activity::getModalDialogManager,
+                                    new OfflinePageUtils.TabOfflinePageLoadUrlDelegate(tab),
+                                    null,
+                                    null,
+                                    ChromePageInfoHighlight.noHighlight(),
+                                    null) {
+                                @Override
+                                public boolean isShowingPaintPreviewPage() {
+                                    return true;
+                                }
+                            };
+                    PageInfoController.show(
+                            mActivityTestRule.getActivity(),
+                            tab.getWebContents(),
+                            null,
+                            PageInfoController.OpenedFromSource.MENU,
+                            pageInfoControllerDelegate,
+                            ChromePageInfoHighlight.noHighlight(),
+                            Gravity.TOP);
+                });
+        onViewWaiting(
+                allOf(withText(R.string.page_info_connection_paint_preview), isDisplayed()), true);
+    }
+
+    /** Tests that page info view is shown correctly for transient pdf pages. */
+    @Test
+    @MediumTest
+    public void testTransientPdfPage() {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    final ChromeActivity activity = mActivityTestRule.getActivity();
+                    final Tab tab = activity.getActivityTab();
+                    ChromePageInfoControllerDelegate pageInfoControllerDelegate =
+                            new ChromePageInfoControllerDelegate(
+                                    activity,
+                                    tab.getWebContents(),
+                                    activity::getModalDialogManager,
+                                    new OfflinePageUtils.TabOfflinePageLoadUrlDelegate(tab),
+                                    null,
+                                    null,
+                                    ChromePageInfoHighlight.noHighlight(),
+                                    null) {
+                                @Override
+                                public @PdfPageType int getPdfPageType() {
+                                    return PdfPageType.TRANSIENT_SECURE;
+                                }
+                            };
+                    PageInfoController.show(
+                            mActivityTestRule.getActivity(),
+                            tab.getWebContents(),
+                            null,
+                            PageInfoController.OpenedFromSource.MENU,
+                            pageInfoControllerDelegate,
+                            ChromePageInfoHighlight.noHighlight(),
+                            Gravity.TOP);
+                });
+        onViewWaiting(
+                allOf(withText(R.string.page_info_connection_transient_pdf), isDisplayed()), true);
+    }
+
+    /** Tests that page info view is shown correctly for insecure transient pdf pages. */
+    @Test
+    @MediumTest
+    public void testInsecureTransientPdfPage() {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    final ChromeActivity activity = mActivityTestRule.getActivity();
+                    final Tab tab = activity.getActivityTab();
+                    ChromePageInfoControllerDelegate pageInfoControllerDelegate =
+                            new ChromePageInfoControllerDelegate(
+                                    activity,
+                                    tab.getWebContents(),
+                                    activity::getModalDialogManager,
+                                    new OfflinePageUtils.TabOfflinePageLoadUrlDelegate(tab),
+                                    null,
+                                    null,
+                                    ChromePageInfoHighlight.noHighlight(),
+                                    null) {
+                                @Override
+                                public @PdfPageType int getPdfPageType() {
+                                    return PdfPageType.TRANSIENT_INSECURE;
+                                }
+                            };
+                    PageInfoController.show(
+                            mActivityTestRule.getActivity(),
+                            tab.getWebContents(),
+                            null,
+                            PageInfoController.OpenedFromSource.MENU,
+                            pageInfoControllerDelegate,
+                            ChromePageInfoHighlight.noHighlight(),
+                            Gravity.TOP);
+                });
+        onViewWaiting(
+                allOf(
+                        withText(R.string.page_info_connection_transient_pdf_insecure),
+                        isDisplayed()),
+                true);
+    }
+
+    /** Tests that page info view is shown correctly for local pdf pages. */
+    @Test
+    @MediumTest
+    public void testLocalPdfPage() {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    final ChromeActivity activity = mActivityTestRule.getActivity();
+                    final Tab tab = activity.getActivityTab();
+                    ChromePageInfoControllerDelegate pageInfoControllerDelegate =
+                            new ChromePageInfoControllerDelegate(
+                                    activity,
+                                    tab.getWebContents(),
+                                    activity::getModalDialogManager,
+                                    new OfflinePageUtils.TabOfflinePageLoadUrlDelegate(tab),
+                                    null,
+                                    null,
+                                    ChromePageInfoHighlight.noHighlight(),
+                                    null) {
+                                @Override
+                                public @PdfPageType int getPdfPageType() {
+                                    return PdfPageType.LOCAL;
+                                }
+                            };
+                    PageInfoController.show(
+                            mActivityTestRule.getActivity(),
+                            tab.getWebContents(),
+                            null,
+                            PageInfoController.OpenedFromSource.MENU,
+                            pageInfoControllerDelegate,
+                            ChromePageInfoHighlight.noHighlight(),
+                            Gravity.TOP);
+                });
+        onViewWaiting(
+                allOf(withText(R.string.page_info_connection_local_pdf), isDisplayed()), true);
+    }
+
     /** Tests PageInfo on a website with permissions and no particular row highlight. */
     @Test
     @MediumTest
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/WebsitePermissionsFetcherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/WebsitePermissionsFetcherTest.java
index 2d22d7a4..24725e9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/WebsitePermissionsFetcherTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/WebsitePermissionsFetcherTest.java
@@ -716,7 +716,7 @@
         // Otherwise, just update count in the assert.
         // TODO(https://b/332704817): Add test for Tracking Protection content setting after Android
         // integration.
-        assertEquals(123, ContentSettingsType.MAX_VALUE);
+        assertEquals(122, ContentSettingsType.MAX_VALUE);
         websitePreferenceBridge.addContentSettingException(
                 new ContentSettingException(
                         ContentSettingsType.COOKIES,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImplTest.java
index 2c2043a..536e2f6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImplTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImplTest.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.tabmodel;
 
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -11,8 +14,12 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstCardFromTabSwitcher;
+import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;
 
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.MediumTest;
@@ -41,11 +48,14 @@
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter.MergeNotificationType;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilterObserver.DidRemoveTabGroupReason;
+import org.chromium.chrome.browser.tabmodel.TabModelActionListener.DialogType;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.R;
 import org.chromium.chrome.test.transit.AutoResetCtaTransitTestRule;
 import org.chromium.chrome.test.transit.ChromeTransitTestRules;
 import org.chromium.chrome.test.transit.page.WebPageStation;
+import org.chromium.components.browser_ui.widget.ActionConfirmationResult;
 import org.chromium.components.tab_groups.TabGroupColorId;
 import org.chromium.content_public.browser.LoadUrlParams;
 
@@ -1231,7 +1241,7 @@
                     @Override
                     public void didMoveTabOutOfGroup(Tab movedTab, int prevFilterIndex) {
                         assertEquals(tab1, movedTab);
-                        assertEquals(0, prevFilterIndex);
+                        assertEquals(1, prevFilterIndex);
                         didMoveOutOfGroup.notifyCalled();
                     }
 
@@ -1239,7 +1249,7 @@
                     public void didRemoveTabGroup(
                             int tabId, Token tabGroupId, @DidRemoveTabGroupReason int reason) {
                         assertEquals(groupId, tabGroupId);
-                        assertEquals(DidRemoveTabGroupReason.PIN, reason);
+                        assertEquals(DidRemoveTabGroupReason.UNGROUP, reason);
                         didRemoveGroup.notifyCalled();
                     }
                 };
@@ -1247,7 +1257,7 @@
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     mCollectionModel.addTabGroupObserver(groupObserver);
-                    mRegularModel.pinTab(tab1.getId());
+                    mRegularModel.pinTab(tab1.getId(), /* showUngroupDialog= */ false);
                     mCollectionModel.removeTabGroupObserver(groupObserver);
                 });
 
@@ -1263,6 +1273,69 @@
     @Test
     @MediumTest
     @EnableFeatures(ChromeFeatureList.ANDROID_PINNED_TABS)
+    public void testPinTabInGroup_ActionListener_Accept() throws Exception {
+        Tab tab0 = getTabAt(0);
+        Tab tab1 = createTab();
+        ThreadUtils.runOnUiThreadBlocking(() -> mCollectionModel.createSingleTabGroup(tab1));
+        assertNotNull(tab1.getTabGroupId());
+        assertTabsInOrderAre(List.of(tab0, tab1));
+
+        TabModelActionListener listener = mock(TabModelActionListener.class);
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    mCollectionModel.pinTab(tab1.getId(), /* showUngroupDialog= */ true, listener);
+                });
+
+        onViewWaiting(withText(R.string.delete_tab_group_action), /* checkRootDialog= */ true)
+                .perform(click());
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    verify(listener)
+                            .onConfirmationDialogResult(
+                                    eq(DialogType.SYNC),
+                                    eq(ActionConfirmationResult.CONFIRMATION_POSITIVE));
+                    assertTrue(tab1.getIsPinned());
+                    assertNull(tab1.getTabGroupId());
+                    assertTabsInOrderAre(List.of(tab1, tab0));
+                });
+    }
+
+    @Test
+    @MediumTest
+    @EnableFeatures(ChromeFeatureList.ANDROID_PINNED_TABS)
+    public void testPinTabInGroup_ActionListener_Reject() throws Exception {
+        Tab tab0 = getTabAt(0);
+        Tab tab1 = createTab();
+        ThreadUtils.runOnUiThreadBlocking(() -> mCollectionModel.createSingleTabGroup(tab1));
+        assertNotNull(tab1.getTabGroupId());
+        assertTabsInOrderAre(List.of(tab0, tab1));
+
+        TabModelActionListener listener = mock(TabModelActionListener.class);
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    mCollectionModel.pinTab(tab1.getId(), /* showUngroupDialog= */ true, listener);
+                });
+
+        onViewWaiting(withText(R.string.cancel), /* checkRootDialog= */ true).perform(click());
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    verify(listener)
+                            .onConfirmationDialogResult(
+                                    eq(DialogType.SYNC),
+                                    eq(ActionConfirmationResult.CONFIRMATION_NEGATIVE));
+                    assertFalse(tab1.getIsPinned());
+                    assertNotNull(tab1.getTabGroupId());
+                    assertTabsInOrderAre(List.of(tab0, tab1));
+                });
+    }
+
+    @Test
+    @MediumTest
+    @EnableFeatures(ChromeFeatureList.ANDROID_PINNED_TABS)
     public void testPinTabInMultiTabGroup() throws Exception {
         Tab tab0 = getTabAt(0);
         Tab tab1 = createTab();
@@ -1286,7 +1359,7 @@
                     @Override
                     public void didMoveTabOutOfGroup(Tab movedTab, int prevFilterIndex) {
                         assertEquals(tab0, movedTab);
-                        assertEquals(1, prevFilterIndex);
+                        assertEquals(0, prevFilterIndex);
                         didMoveOutOfGroup.notifyCalled();
                     }
 
@@ -1299,7 +1372,7 @@
                 () -> {
                     mCollectionModel.addTabGroupObserver(groupObserver);
 
-                    mRegularModel.pinTab(tab0.getId());
+                    mRegularModel.pinTab(tab0.getId(), /* showUngroupDialog= */ false);
 
                     mCollectionModel.removeTabGroupObserver(groupObserver);
 
@@ -2671,7 +2744,7 @@
                 () -> {
                     mRegularModel.addObserver(observer);
                     if (isPinned) {
-                        mRegularModel.pinTab(changedTab.getId());
+                        mRegularModel.pinTab(changedTab.getId(), /* showUngroupDialog= */ false);
                     } else {
                         mRegularModel.unpinTab(changedTab.getId());
                     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelImplTest.java
index 98c5c30..8489b3b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelImplTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelImplTest.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.tabmodel;
 
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.assertEquals;
@@ -13,12 +16,14 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import static org.chromium.chrome.test.util.ChromeTabUtils.getIndexOnUiThread;
 import static org.chromium.chrome.test.util.ChromeTabUtils.getTabCountOnUiThread;
+import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -54,6 +59,7 @@
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.R;
 import org.chromium.chrome.test.transit.AutoResetCtaTransitTestRule;
 import org.chromium.chrome.test.transit.ChromeTransitTestRules;
 import org.chromium.chrome.test.transit.Journeys;
@@ -61,6 +67,7 @@
 import org.chromium.chrome.test.transit.page.WebPageStation;
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge;
+import org.chromium.components.browser_ui.widget.ActionConfirmationResult;
 import org.chromium.components.content_settings.ContentSetting;
 import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.content_public.common.ResourceRequestBody;
@@ -92,6 +99,8 @@
 
     @Mock private TabModelObserver mTabModelObserver;
 
+    @Mock private TabModelActionListener mTabModelActionListener;
+
     private String mTestUrl;
     private WebPageStation mPage;
     private TabModelJniBridge mTabModelJni;
@@ -1114,7 +1123,7 @@
                     TabModel tabModel =
                             mActivityTestRule.getActivity().getTabModelSelector().getModel(false);
                     Tab tabToPin = tabModel.getTabAt(2);
-                    tabModel.pinTab(tabToPin.getId());
+                    tabModel.pinTab(tabToPin.getId(), /* showUngroupDialog= */ false);
 
                     assertTrue(tabToPin.getIsPinned());
                     assertEquals(tabToPin.getId(), tabModel.getTabAt(0).getId());
@@ -1138,11 +1147,11 @@
                     Tab tab1 = tabModel.getTabAt(1);
                     Tab tab2 = tabModel.getTabAt(2);
 
-                    tabModel.pinTab(tab1.getId());
+                    tabModel.pinTab(tab1.getId(), /* showUngroupDialog= */ false);
 
                     assertEquals(tab1, tabModel.getTabAt(0));
 
-                    tabModel.pinTab(tab2.getId());
+                    tabModel.pinTab(tab2.getId(), /* showUngroupDialog= */ false);
 
                     assertEquals(tab1, tabModel.getTabAt(0));
                     assertEquals(tab2, tabModel.getTabAt(1));
@@ -1173,9 +1182,9 @@
 
                     // Pin last 3 tabs.
                     // [D], [C], [B], A.
-                    tabModel.pinTab(tab3.getId());
-                    tabModel.pinTab(tab2.getId());
-                    tabModel.pinTab(tab1.getId());
+                    tabModel.pinTab(tab3.getId(), /* showUngroupDialog= */ false);
+                    tabModel.pinTab(tab2.getId(), /* showUngroupDialog= */ false);
+                    tabModel.pinTab(tab1.getId(), /* showUngroupDialog= */ false);
 
                     // Unpin the middle tab (Tab2).
                     // [D], [B], C, A.
@@ -1210,8 +1219,8 @@
 
                     // Pin 2 tabs.
                     // [B], [C], A, D.
-                    tabModel.pinTab(tab1.getId());
-                    tabModel.pinTab(tab2.getId());
+                    tabModel.pinTab(tab1.getId(), /* showUngroupDialog= */ false);
+                    tabModel.pinTab(tab2.getId(), /* showUngroupDialog= */ false);
                     assertTrue(tab1.getIsPinned());
                     assertTrue(tab2.getIsPinned());
 
@@ -1245,13 +1254,13 @@
                     Tab tab1 = tabModel.getTabAt(1);
                     Tab tab2 = tabModel.getTabAt(2);
 
-                    tabModel.pinTab(tab2.getId());
+                    tabModel.pinTab(tab2.getId(), /* showUngroupDialog= */ false);
 
                     verify(mTabModelObserver).didMoveTab(tab2, 0, 2);
                     verify(mTabModelObserver).willChangePinState(tab2);
                     verify(mTabModelObserver).didChangePinState(tab2);
 
-                    tabModel.pinTab(tab1.getId());
+                    tabModel.pinTab(tab1.getId(), /* showUngroupDialog= */ false);
 
                     verify(mTabModelObserver).didMoveTab(tab1, 1, 2);
                     verify(mTabModelObserver).willChangePinState(tab1);
@@ -1668,17 +1677,17 @@
                     Tab tab2 = mTabModelJni.getTabAt(2);
 
                     // Pin tab1. Order should be tab1, tab0, tab2
-                    mTabModelJni.pinTab(tab1.getId());
+                    mTabModelJni.pinTab(tab1.getId(), /* showUngroupDialog= */ false);
                     assertTrue(tab1.getIsPinned());
                     assertEquals(0, mTabModelJni.indexOf(tab1));
                     assertEquals(1, mTabModelJni.indexOf(tab0));
                     assertEquals(2, mTabModelJni.indexOf(tab2));
 
                     // Pin tab1 again. Order should not change.
-                    mTabModelJni.pinTab(tab1.getId());
+                    mTabModelJni.pinTab(tab1.getId(), /* showUngroupDialog= */ false);
 
                     // Pin tab2. It should move to right place
-                    mTabModelJni.pinTab(tab2.getId());
+                    mTabModelJni.pinTab(tab2.getId(), /* showUngroupDialog= */ false);
                     assertTrue(tab2.getIsPinned());
                     assertEquals(0, mTabModelJni.indexOf(tab1));
                     assertEquals(1, mTabModelJni.indexOf(tab2));
@@ -1758,10 +1767,10 @@
 
                     assertEquals(0, tabModel.getPinnedTabsCount());
 
-                    tabModel.pinTab(tab1.getId());
+                    tabModel.pinTab(tab1.getId(), /* showUngroupDialog= */ false);
                     assertEquals(1, tabModel.getPinnedTabsCount());
 
-                    tabModel.pinTab(tab2.getId());
+                    tabModel.pinTab(tab2.getId(), /* showUngroupDialog= */ false);
                     assertEquals(2, tabModel.getPinnedTabsCount());
 
                     tabModel.unpinTab(tab1.getId());
@@ -1788,9 +1797,9 @@
                     Tab tab2 = tabModel.getTabAt(2);
                     Tab tab3 = tabModel.getTabAt(3);
 
-                    tabModel.pinTab(tab1.getId());
-                    tabModel.pinTab(tab2.getId());
-                    tabModel.pinTab(tab3.getId());
+                    tabModel.pinTab(tab1.getId(), /* showUngroupDialog= */ false);
+                    tabModel.pinTab(tab2.getId(), /* showUngroupDialog= */ false);
+                    tabModel.pinTab(tab3.getId(), /* showUngroupDialog= */ false);
 
                     assertEquals(0, tabModel.indexOf(tab1));
                     assertEquals(1, tabModel.indexOf(tab2));
@@ -1986,11 +1995,81 @@
                                     .build();
 
                     // Pin and unpin the tab.
-                    tabModel.pinTab(tabUnderInvestigation.getId());
+                    tabModel.pinTab(tabUnderInvestigation.getId(), /* showUngroupDialog= */ false);
                     tabModel.unpinTab(tabUnderInvestigation.getId());
 
                     // Verify that the histogram was recorded.
                     histogram.assertExpected();
                 });
     }
+
+    @Test
+    @SmallTest
+    @EnableFeatures(ChromeFeatureList.ANDROID_PINNED_TABS)
+    public void testPinTabInGroup_ActionListener_Accept() {
+        TabGroupModelFilter filter = mPage.getTabGroupModelFilter();
+        createTabGroup(1, filter); // Group with 1 tab.
+        // 0:Tab0 | Group0: 1:Tab1
+
+        Tab tab1 = ThreadUtils.runOnUiThreadBlocking(() -> mTabModelJni.getTabAt(1));
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    assertNotNull(tab1.getTabGroupId());
+
+                    mTabModelJni.pinTab(
+                            tab1.getId(), /* showUngroupDialog= */ true, mTabModelActionListener);
+                });
+        onViewWaiting(withText(R.string.delete_tab_group_action), /* checkRootDialog= */ true)
+                .perform(click());
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    verify(mTabModelActionListener)
+                            .onConfirmationDialogResult(
+                                    eq(TabModelActionListener.DialogType.SYNC),
+                                    eq(ActionConfirmationResult.CONFIRMATION_POSITIVE));
+                    assertTrue(tab1.getIsPinned());
+                    assertNull(tab1.getTabGroupId());
+
+                    // [1:Tab1] | 0:Tab0
+                    assertEquals(0, mTabModelJni.indexOf(tab1));
+
+                    // Cleanup
+                    mTabModelJni.unpinTab(tab1.getId());
+                });
+    }
+
+    @Test
+    @SmallTest
+    @EnableFeatures(ChromeFeatureList.ANDROID_PINNED_TABS)
+    public void testPinTabInGroup_ActionListener_Reject() {
+        TabGroupModelFilter filter = mPage.getTabGroupModelFilter();
+        createTabGroup(1, filter); // Group with 1 tab.
+        // 0:Tab0 | Group0: 1:Tab1
+
+        Tab tab1 = ThreadUtils.runOnUiThreadBlocking(() -> mTabModelJni.getTabAt(1));
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    assertNotNull(tab1.getTabGroupId());
+
+                    mTabModelJni.pinTab(
+                            tab1.getId(), /* showUngroupDialog= */ true, mTabModelActionListener);
+                });
+        onViewWaiting(withText(R.string.cancel), /* checkRootDialog= */ true).perform(click());
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    TabModel tabModel =
+                            mActivityTestRule.getActivity().getTabModelSelector().getModel(false);
+                    verify(mTabModelActionListener)
+                            .onConfirmationDialogResult(
+                                    eq(TabModelActionListener.DialogType.SYNC),
+                                    eq(ActionConfirmationResult.CONFIRMATION_NEGATIVE));
+                    assertEquals(1, mTabModelJni.indexOf(tab1));
+                    assertFalse(tab1.getIsPinned());
+                    assertNotNull(tab1.getTabGroupId());
+                });
+    }
 }
diff --git a/chrome/android/junit/BUILD.gn b/chrome/android/junit/BUILD.gn
index 2d65214..78a3956 100644
--- a/chrome/android/junit/BUILD.gn
+++ b/chrome/android/junit/BUILD.gn
@@ -1102,6 +1102,7 @@
       "//chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImplUnitTest.java",
       "//chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImplUtilUnitTest.java",
       "//chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelRemoverUnitTest.java",
+      "//chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPinnerActionListenerUnitTest.java",
       "//chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabRemoverImplUnitTest.java",
       "//chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabUngrouperImplUnitTest.java",
       "src/org/chromium/chrome/browser/tabmodel/ArchivedTabModelSelectorImplTest.java",
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinatorUnitTest.java
index 2cd49c3..171d6d1 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinatorUnitTest.java
@@ -539,6 +539,26 @@
     @Feature("Tab Strip Context Menu")
     @EnableFeatures(ChromeFeatureList.SUBMENUS_TAB_CONTEXT_MENU_LFF_TAB_STRIP)
     @SuppressWarnings("DirectInvocationOnMock")
+    public void testAddToGroupSubmenu_fallbackTabGroupName_incognito() {
+        setupWithIncognito(true);
+        initializeCoordinator();
+        when(mTabGroupModelFilter.getTabGroupTitle(TAB_GROUP_ID)).thenReturn("");
+        MultiWindowUtils.setInstanceCountForTesting(1);
+        mSavedTabGroup.title = "";
+        var modelList = new ModelList();
+        mTabContextMenuCoordinator.configureMenuItemsForTesting(
+                modelList, Collections.singletonList(TAB_OUTSIDE_OF_GROUP_ID));
+
+        assertEquals("Number of items in the list menu is incorrect", 5, modelList.size());
+
+        // List item 1
+        verifyAddToGroupSubmenuForTabOutsideOfGroup(modelList, "1 tab", 1);
+    }
+
+    @Test
+    @Feature("Tab Strip Context Menu")
+    @EnableFeatures(ChromeFeatureList.SUBMENUS_TAB_CONTEXT_MENU_LFF_TAB_STRIP)
+    @SuppressWarnings("DirectInvocationOnMock")
     public void testListMenuItems_tabOutsideOfGroup_multipleWindows() {
         MultiWindowUtils.setInstanceCountForTesting(3);
         when(mMultiInstanceManager.getInstanceInfo(ACTIVE))
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/MultiTabReorderStrategyTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/MultiTabReorderStrategyTest.java
index a97629d..f72f86b8 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/MultiTabReorderStrategyTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/MultiTabReorderStrategyTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -117,6 +118,11 @@
         when(mTabGroupModelFilter.getTabUngrouper()).thenReturn(mTabUnGrouper);
     }
 
+    @After
+    public void tearDown() {
+        mStrategy.clearReorderStateForTesting();
+    }
+
     @Override
     protected void setupStripViews() {
         // Layout: [Tab1] [ExpandedGroup]([Tab2][Tab3]) [Tab4(CollapsedGroup)] [Tab5] [Tab6]
@@ -273,6 +279,7 @@
         selectTabs(mUngroupedTab2, mUngroupedTab3);
         float rebuildDeltaX = -TAB_WIDTH;
         float dragDeltaX = -DRAG_PAST_COLLAPSED_GROUP_SUCCESS;
+        startReorder(mUngroupedTab2);
         testUpdateReorder_success(
                 mUngroupedTab2, rebuildDeltaX, dragDeltaX, mCollapsedGroupTitle, 5);
         verifyBlockMovedPastGroup();
@@ -322,7 +329,6 @@
         mUngroupedTab3.setIsPinned(true);
         selectTabs(mUngroupedTab2);
         startReorder(mUngroupedTab2);
-        reset(mModel, mTabGroupModelFilter.getTabUngrouper());
         testUpdateReorder_fail(mUngroupedTab2, DRAG_PAST_TAB_SUCCESS);
     }
 
@@ -334,7 +340,6 @@
         mUngroupedTab2.setIsPinned(true);
         selectTabs(mUngroupedTab2);
         startReorder(mUngroupedTab2);
-        reset(mModel, mTabGroupModelFilter.getTabUngrouper());
         testUpdateReorder_fail(mUngroupedTab2, DRAG_PAST_TAB_SUCCESS);
     }
 
@@ -343,7 +348,6 @@
     public void testUpdateReorder_fail_pastTab() {
         selectTabs(mUngroupedTab2);
         startReorder(mUngroupedTab2);
-        reset(mModel, mTabGroupModelFilter.getTabUngrouper());
         testUpdateReorder_fail(mUngroupedTab2, DRAG_PAST_TAB_FAIL);
     }
 
@@ -352,7 +356,6 @@
     public void testUpdateReorder_fail_pastCollapsedGroup() {
         selectTabs(mUngroupedTab2, mUngroupedTab3);
         startReorder(mUngroupedTab2);
-        reset(mModel, mTabGroupModelFilter.getTabUngrouper());
         testUpdateReorder_fail(mUngroupedTab2, -DRAG_PAST_COLLAPSED_GROUP_FAIL);
     }
 
@@ -361,7 +364,6 @@
     public void testUpdateReorder_fail_mergeIntoGroup() {
         selectTabs(mUngroupedTab1);
         startReorder(mUngroupedTab1);
-        reset(mModel, mTabGroupModelFilter.getTabUngrouper());
         testUpdateReorder_fail(mUngroupedTab1, DRAG_INTO_GROUP_FAIL);
     }
 
@@ -370,7 +372,6 @@
     public void testUpdateReorder_fail_dragOutOfGroup() {
         selectTabs(mGroupedTab1, mGroupedTab2);
         startReorder(mGroupedTab1);
-        reset(mModel, mTabGroupModelFilter.getTabUngrouper());
 
         when(mTabGroupModelFilter.isTabInTabGroup(mModel.getTabById(TAB_ID2))).thenReturn(true);
         when(mTabGroupModelFilter.isTabInTabGroup(mModel.getTabById(TAB_ID3))).thenReturn(true);
@@ -429,7 +430,6 @@
             StripLayoutView viewToMove,
             int expectedModelIndex) {
         mockRebuildForBlockMove(viewToMove, rebuildDeltaX);
-        startReorder(primaryTab);
         drag(dragDeltaX);
 
         verify(mAnimationHost, times(2)).startAnimations(anyList(), isNull());
@@ -451,7 +451,6 @@
     }
 
     private void testUpdateReorder_fail(StripLayoutTab primaryTab, float dragDeltaX) {
-        startReorder(primaryTab);
         drag(dragDeltaX);
         verifyFailedDrag(dragDeltaX);
     }
@@ -496,12 +495,11 @@
 
     @SuppressWarnings("DirectInvocationOnMock")
     private void verifyFailedDrag(float expectedOffset) {
-        verify(mModel, never()).moveTab(anyInt(), anyInt());
         verify(mTabGroupModelFilter, never()).moveRelatedTabs(anyInt(), anyInt());
         verify(mTabGroupModelFilter.getTabUngrouper(), times(1))
                 .ungroupTabs(anyList(), anyBoolean(), anyBoolean(), any());
 
-        verify(mAnimationHost, times(2)).startAnimations(anyList(), isNull());
+        verify(mAnimationHost, times(1)).startAnimations(anyList(), isNull());
 
         for (StripLayoutTab tab : mSelectedTabs) {
             assertEquals(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java
index 3f9e22e..ceba93a2 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.customtabs.content;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -37,6 +38,7 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.app.tabmodel.AsyncTabParamsManagerSingleton;
 import org.chromium.chrome.browser.autofill.AndroidAutofillAvailabilityStatus;
 import org.chromium.chrome.browser.autofill.AutofillClientProviderUtils;
 import org.chromium.chrome.browser.cookies.CookiesFetcher;
@@ -355,9 +357,23 @@
     @Test
     public void doesNotUseTabFromIntent_IfNotInAsyncParamsManager() {
         Tab tab = env.prepareTransferredTab();
+        AsyncTabParamsManagerSingleton.getInstance().remove(tab.getId());
         mTabController.setUpInitialTab(null);
         mTabController.finishNativeInitialization();
-        assertEquals(tab, env.tabProvider.getTab());
+        assertNotEquals(tab, env.tabProvider.getTab());
+    }
+
+    // If the Activity has been recreated, ignore the Tab ID provided in the Intent -- the Tab will
+    // be restored using a different mechanism. See crbug.com/448865648.
+    @Test
+    public void doesNotUseTabFromIntent_IfActivityRecreated() {
+        Tab popupTab = env.prepareTransferredTab();
+        Tab savedTab = env.prepareTab();
+        env.saveTab(savedTab);
+        mTabController.setUpInitialTab(null);
+        mTabController.finishNativeInitialization();
+        assertEquals(savedTab, env.tabProvider.getTab());
+        assertEquals(TabCreationMode.RESTORED, env.tabProvider.getInitialTabCreationMode());
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/notifications/tips/TipsPromoCoordinatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/notifications/tips/TipsPromoCoordinatorUnitTest.java
index b4b30f6..f1f945fc 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/notifications/tips/TipsPromoCoordinatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/notifications/tips/TipsPromoCoordinatorUnitTest.java
@@ -25,6 +25,7 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.notifications.scheduler.TipsNotificationsFeatureType;
 import org.chromium.chrome.browser.notifications.tips.TipsPromoProperties.ScreenType;
@@ -32,6 +33,8 @@
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.ui.modelutil.PropertyModel;
 
+import java.util.concurrent.TimeoutException;
+
 /** Unit tests for {@link TipsPromoCoordinator}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
@@ -63,8 +66,11 @@
 
     @SmallTest
     @Test
-    public void testShowBottomSheet() {
-        mTipsPromoCoordinator.showBottomSheet(TipsNotificationsFeatureType.ENHANCED_SAFE_BROWSING);
+    public void testShowBottomSheet() throws TimeoutException {
+        CallbackHelper callbackHelper = new CallbackHelper();
+        mTipsPromoCoordinator.showBottomSheet(
+                TipsNotificationsFeatureType.ENHANCED_SAFE_BROWSING, callbackHelper::notifyCalled);
+
         assertEquals(
                 ScreenType.MAIN_SCREEN, mPropertyModel.get(TipsPromoProperties.CURRENT_SCREEN));
 
@@ -73,6 +79,10 @@
                 ScreenType.DETAIL_SCREEN, mPropertyModel.get(TipsPromoProperties.CURRENT_SCREEN));
 
         verify(mBottomSheetController).requestShowContent(any(), eq(true));
+
+        mView.findViewById(R.id.tips_promo_settings_button).performClick();
+        verify(mBottomSheetController).hideContent(any(), eq(true));
+        callbackHelper.waitForOnly();
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/notifications/tips/TipsPromoViewBinderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/notifications/tips/TipsPromoViewBinderUnitTest.java
index 124aca8..3de3687e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/notifications/tips/TipsPromoViewBinderUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/notifications/tips/TipsPromoViewBinderUnitTest.java
@@ -97,4 +97,23 @@
         onClickListener.performClick();
         callbackHelper.waitForOnly();
     }
+
+    @SmallTest
+    @Test
+    public void testSettingsButtonClickListener() throws TimeoutException {
+        CallbackHelper callbackHelper = new CallbackHelper();
+        OnClickListener clickListener = (view) -> callbackHelper.notifyCalled();
+
+        mModel.set(TipsPromoProperties.SETTINGS_BUTTON_CLICK_LISTENER, clickListener);
+        View settingsOnClickListener = mView.findViewById(R.id.tips_promo_settings_button);
+        assertNotNull(settingsOnClickListener);
+        settingsOnClickListener.performClick();
+        callbackHelper.waitForNext();
+
+        View settingsDetailsOnClickListener =
+                mView.findViewById(R.id.tips_promo_details_settings_button);
+        assertNotNull(settingsDetailsOnClickListener);
+        settingsDetailsOnClickListener.performClick();
+        callbackHelper.waitForNext();
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAdaptiveToolbarBehaviorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAdaptiveToolbarBehaviorTest.java
index 64a48b6..d7bce9b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAdaptiveToolbarBehaviorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAdaptiveToolbarBehaviorTest.java
@@ -29,7 +29,7 @@
         Activity activity = Robolectric.setupActivity(Activity.class);
         mBehavior =
                 new TabbedAdaptiveToolbarBehavior(
-                        activity, null, null, null, null, null, null, null, null, null);
+                        activity, null, null, null, null, null, null, null, null, null, null);
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ui/system/StatusBarColorControllerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ui/system/StatusBarColorControllerUnitTest.java
index 7accc3f..61036f6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ui/system/StatusBarColorControllerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ui/system/StatusBarColorControllerUnitTest.java
@@ -39,7 +39,7 @@
 import org.chromium.chrome.browser.layouts.LayoutManager;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.ntp_customization.NtpCustomizationConfigManager;
-import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils;
+import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiThemeUtil;
 import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
@@ -220,7 +220,7 @@
         NtpCustomizationConfigManager ntpCustomizationConfigManager =
                 NtpCustomizationConfigManager.getInstance();
         ntpCustomizationConfigManager.setBackgroundImageTypeForTesting(
-                NtpCustomizationUtils.NtpBackgroundImageType.CHROME_COLOR);
+                NtpBackgroundImageType.CHROME_COLOR);
         ntpCustomizationConfigManager.setBackgroundColorForTesting(currentNtpBackground);
 
         // Verifies when customized NTP background isn't supported, the status bar color is set to
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 15b7622..6a321e5 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -8563,7 +8563,7 @@
         Add file
       </message>
       <message name="IDS_NTP_COMPOSE_ADD_CONTEXT" desc="The text to display to ask user to add context to their search e.g. pdf or image.">
-        Add tabs and more
+        Add a tab and more
       </message>
       <message name="IDS_NTP_COMPOSE_ADD_CONTEXT_TITLE" desc="The a11y title for a button that allows users to add context to their search e.g. pdf or image.">
         Add a tab or file to your search, and also use a tool
@@ -9789,6 +9789,9 @@
       <message name="IDS_TOOLTIP_LENS_SEARCH" desc="The tooltip for search-by-image button">
         Search by image
       </message>
+      <message name="IDS_TOOLTIP_LENS_REINVOKE_VISUAL_SELECTION_A11Y_LABEL" desc="The a11y label for the reinvoke visual selection button in the compose box.">
+        Select region of screen for follow up
+      </message>
       <message name="IDS_LENS_SEARCH_UPLOAD_DIALOG_CLOSE_BUTTON_LABEL" desc="Aria label for the button to close the upload dialog.">
         Close
       </message>
@@ -16314,6 +16317,9 @@
       <message name="IDS_DESKTOP_MEDIA_PICKER_ALSO_SHARE_SYSTEM_AUDIO" desc="Text for the audio toggle in the window picker dialog which is displayed when attempting to share a screen.">
         Also share system audio
       </message>
+      <message name="IDS_DESKTOP_MEDIA_PICKER_ALSO_SHARE_APPLICATION_AUDIO" desc="Text for the audio toggle in the window picker dialog which is displayed when attempting to share a window.">
+        Also share application audio
+      </message>
       <message name="IDS_DESKTOP_MEDIA_PICKER_ALSO_SHARE_ALL_AUDIO_OUTPUT" desc="Text for the audio toggle in the window picker dialog which is displayed when attempting to share a screen. This string is used when we share all output audio devices, rather than just the default device">
         Also share all audio outputs
       </message>
diff --git a/chrome/app/generated_resources_grd/IDS_DESKTOP_MEDIA_PICKER_ALSO_SHARE_APPLICATION_AUDIO.png.sha1 b/chrome/app/generated_resources_grd/IDS_DESKTOP_MEDIA_PICKER_ALSO_SHARE_APPLICATION_AUDIO.png.sha1
new file mode 100644
index 0000000..2b15b6b
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DESKTOP_MEDIA_PICKER_ALSO_SHARE_APPLICATION_AUDIO.png.sha1
@@ -0,0 +1 @@
+bae07903a9200dbaf90ffa09831d029efd9defd8
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_ADD_CONTEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_ADD_CONTEXT.png.sha1
index 04adb3e1..a93d8c2d 100644
--- a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_ADD_CONTEXT.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_ADD_CONTEXT.png.sha1
@@ -1 +1 @@
-c11cafcc8cf334224c088521bafbb88449eb80bb
\ No newline at end of file
+4c4ab562b1a0f6bf38ea452c05d62fd8ec041e2f
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_TOOLTIP_LENS_REINVOKE_VISUAL_SELECTION_A11Y_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_TOOLTIP_LENS_REINVOKE_VISUAL_SELECTION_A11Y_LABEL.png.sha1
new file mode 100644
index 0000000..db9fb1c
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_TOOLTIP_LENS_REINVOKE_VISUAL_SELECTION_A11Y_LABEL.png.sha1
@@ -0,0 +1 @@
+e71778218c9d2a0112640448d2207b3ccecfd062
\ No newline at end of file
diff --git a/chrome/app/resources/generated_resources_ar.xtb b/chrome/app/resources/generated_resources_ar.xtb
index d358a8fa..da7096d 100644
--- a/chrome/app/resources/generated_resources_ar.xtb
+++ b/chrome/app/resources/generated_resources_ar.xtb
@@ -4935,6 +4935,7 @@
 <translation id="437809255587011096">الإعلان عن نمط النص</translation>
 <translation id="4378154925671717803">هاتف</translation>
 <translation id="4378308539633073595">التمرير للأمام</translation>
+<translation id="4378363201814870400">يمكنك إدارة معلومات الملء التلقائي لتصفّح الإنترنت بشكل أسرع، مثل كلمات المرور وطرق الدفع والعناوين وبطاقات تعريف الهوية</translation>
 <translation id="4378551569595875038">جارٍ الاتصال...</translation>
 <translation id="4378556263712303865">طلب الجهاز</translation>
 <translation id="4378589147979399051">التحكّم في مؤشر الماوس باستخدام لوحة المفاتيح الأساسية</translation>
diff --git a/chrome/app/resources/generated_resources_as.xtb b/chrome/app/resources/generated_resources_as.xtb
index f10931f..18ab4fb6 100644
--- a/chrome/app/resources/generated_resources_as.xtb
+++ b/chrome/app/resources/generated_resources_as.xtb
@@ -4942,6 +4942,7 @@
 <translation id="437809255587011096">পাঠৰ শৈলী ঘোষণা কৰক</translation>
 <translation id="4378154925671717803">ফ'ন</translation>
 <translation id="4378308539633073595">আগলৈ স্ক্ৰ’ল কৰক</translation>
+<translation id="4378363201814870400">দ্ৰুত ব্ৰাউজিঙৰ বাবে আপোনাৰ স্বয়ংক্ৰিয়ভাৱে পূৰ হোৱাৰ সুবিধাৰ তথ্য পৰিচালনা কৰক — পাছৱৰ্ড, পৰিশোধ পদ্ধতি, ঠিকনা আৰু আইডি কাৰ্ডৰ দৰে তথ্য</translation>
 <translation id="4378551569595875038">সংযোগ হৈছে...</translation>
 <translation id="4378556263712303865">ডিভাইচৰ আৱশ্যকতা</translation>
 <translation id="4378589147979399051">আপোনাৰ মুখ্য কীব’ৰ্ডৰ জৰিয়তে আপোনাৰ মাউছৰ কাৰ্ছৰডাল নিয়ন্ত্ৰণ কৰক</translation>
diff --git a/chrome/app/resources/generated_resources_fa.xtb b/chrome/app/resources/generated_resources_fa.xtb
index e45a919..ba05aecf 100644
--- a/chrome/app/resources/generated_resources_fa.xtb
+++ b/chrome/app/resources/generated_resources_fa.xtb
@@ -4942,6 +4942,7 @@
 <translation id="437809255587011096">اعلام سبک‌پردازی نوشتار</translation>
 <translation id="4378154925671717803">تلفن</translation>
 <translation id="4378308539633073595">پیمایش به‌جلو</translation>
+<translation id="4378363201814870400">برای مرور سریع‌تر، اطلاعات تکمیل خودکار را مدیریت کنید — اطلاعاتی مانند گذرواژه‌ها، روش‌های پرداخت، نشانی‌ها، و کارت‌های شناسایی</translation>
 <translation id="4378551569595875038">در حال اتصال..</translation>
 <translation id="4378556263712303865">درخواست دستگاه</translation>
 <translation id="4378589147979399051">کنترل کردن مکان‌نمای موشواره با صفحه‌کلید اصلی</translation>
diff --git a/chrome/app/resources/generated_resources_iw.xtb b/chrome/app/resources/generated_resources_iw.xtb
index ea9267c..db57b3b 100644
--- a/chrome/app/resources/generated_resources_iw.xtb
+++ b/chrome/app/resources/generated_resources_iw.xtb
@@ -1000,6 +1000,7 @@
 <translation id="166278006618318542">אלגוריתם מפתח ציבורי של נושא</translation>
 <translation id="1662801900924515589">האפליקציה <ph name="APP" /> הותקנה</translation>
 <translation id="1663698992894057019">‏כדאי לשדרג למכשיר Chromebook חדש כדי לקבל את גרסאות התוכנה והאבטחה העדכניות</translation>
+<translation id="1664497218584335032">האתר רוצה לפתוח את הקישור הזה באפליקציה.</translation>
 <translation id="166454475160289583">אין הרשאה לשנות את ברירת המחדל של קלט המקלדת</translation>
 <translation id="1665328953287874063">צריך להשתמש בסיסמה או בקוד אימות כדי לבטל את הנעילה של <ph name="DEVICE_TYPE" /></translation>
 <translation id="1665859804801131136">אקספרסיוניזם</translation>
@@ -1858,7 +1859,7 @@
 <translation id="225614027745146050">ברוכים הבאים</translation>
 <translation id="2257053455312861282">הוספת חשבון בית ספרי מאפשרת לתלמיד או לתלמידה להיכנס בנוחות לאתרים, לתוספים ולאפליקציות, ועדיין לפעול במסגרת המגבלות של בקרת ההורים.</translation>
 <translation id="225716114209817872">טשטוש מקסימלי</translation>
-<translation id="2259337945538989999">‏צריך לאמת את הזהות שלך כדי להעביר כרטיסיות מהמכשירים האחרים שלך. בהגדרות אפשר תמיד להפסיק את הסנכרון. ‫Google עשויה להתאים אישית את החיפוש ושירותים נוספים על סמך ההיסטוריה שלך.</translation>
+<translation id="2259337945538989999">‏כדי להעביר כרטיסיות מהמכשירים האחרים שלך, עליך לאמת את הזהות שלך. תמיד אפשר להיכנס להגדרות ולהפסיק את הסנכרון. ‫Google עשויה להתבסס על ההיסטוריה שלך כדי להתאים בשבילך את החיפוש ושירותים נוספים.</translation>
 <translation id="2261323523305321874">מנהל המערכת ביצע שינוי כללי במערכת שמשבית חלק מהפרופילים הישנים.</translation>
 <translation id="22614517036276112">המסמך הזה או המכשיר שלך לא עומדים בחלק מהכללים של מדיניות האבטחה של הארגון. עליך לברר עם האדמין מה צריך לתקן.</translation>
 <translation id="2262477216570151239">השהיה לפני חזרה</translation>
@@ -2884,6 +2885,7 @@
 <translation id="2950666755714083615">אני רוצה להירשם</translation>
 <translation id="2953210795988451570">‏אין יותר עדכוני אבטחה. מומלץ לשדרג למכשיר Chromebook חדש.</translation>
 <translation id="2953218713108551165">לאתר <ph name="SITE" /> אין הרשאה לשלוח התראות. תוצג בקשה נוספת בביקור הבא שלך.</translation>
+<translation id="2955048943983973430">‏שירותי Google קשורים</translation>
 <translation id="2956070239128776395">הקטע מוצב בתוך קבוצה: <ph name="ERROR_LINE" /></translation>
 <translation id="2957124229512318478">‏כדי להשתמש בתוסף הזה, צריך להפעיל את מצב הפיתוח. אי אפשר לבדוק את התוסף הזה בחנות האינטרנט של Chrome.</translation>
 <translation id="2958721676848865875">אזהרת 'אריזת תוסף'</translation>
@@ -4640,6 +4642,7 @@
 <translation id="4151503145138736576">אין אחסון אופליין לניקוי</translation>
 <translation id="4152011295694446843">הסימניות זמינות כאן</translation>
 <translation id="4152670763139331043">{NUM_TABS,plural, =1{כרטיסייה אחת}one{# כרטיסיות}two{# כרטיסיות}other{# כרטיסיות}}</translation>
+<translation id="4153858337812834972">הכרטיסיות האחרונות</translation>
 <translation id="4154658846204884961">כוכב שביט</translation>
 <translation id="4154664944169082762">טביעות אצבע</translation>
 <translation id="4156297721709710037">‫<ph name="WINDOW_TITLE" /> – תצוגה ימנית</translation>
@@ -4801,7 +4804,7 @@
 <translation id="4276856098224910511">לא ניתן להתקין. עדכון מערכת ההפעלה בהמתנה. צריך להחיל עדכונים של מערכת ההפעלה שנמצאים בהמתנה, להפעיל מחדש ולנסות שוב. קוד השגיאה הוא <ph name="ERROR" />.</translation>
 <translation id="4277434192562187284">‏מקור להגדרות XML</translation>
 <translation id="4278348589087554892">{NUM_SITES,plural, =1{ההרשאות הוסרו מאתר אחד}one{ההרשאות הוסרו מ-{NUM_SITES} אתרים}two{ההרשאות הוסרו מ-{NUM_SITES} אתרים}other{ההרשאות הוסרו מ-{NUM_SITES} אתרים}}</translation>
-<translation id="4278390842282768270">מותר</translation>
+<translation id="4278390842282768270">יש אישור</translation>
 <translation id="4278498748067682896">‏השימוש בשדרוג של הקיוסק והשילוט מאפשר למכשיר לפעול רק במצב קיוסק או שילוט. אם רוצים לאפשר למשתמשים להיכנס לחשבון במכשיר, צריך לחזור אחורה ולבצע רישום באמצעות שדרוג ל-Chrome Enterprise.</translation>
 <translation id="4278779213160967034">התהליך הזה עשוי להימשך כמה דקות. הקבצים בתהליך הורדה.</translation>
 <translation id="4279129444466079448">‏ניתן להתקין עד <ph name="PROFILE_LIMIT" /> פרופילים של eSIM במכשיר הזה. כדי להוסיף פרופיל נוסף, קודם צריך להסיר פרופיל קיים.</translation>
@@ -6562,10 +6565,12 @@
 <translation id="5517304475148761050">‏האפליקציה הזו צריכה גישה אל חנות Play</translation>
 <translation id="5517412723934627386"><ph name="NETWORK_TYPE" /> – <ph name="NETWORK_DISPLAY_NAME" /></translation>
 <translation id="5519195206574732858">LTE</translation>
+<translation id="5519766046604311137">תמיד לפתוח קישורים של <ph name="PROTOCOL_SCHEME" /> באפליקציה הזו</translation>
 <translation id="5520605865175417976">הסרת הפעולה <ph name="ACTION_TO_REMOVE" /></translation>
 <translation id="5521078259930077036">האם זהו דף הבית שציפית לראות?</translation>
 <translation id="5522156646677899028">התוסף הזה מכיל נקודת תורפה חמורה באבטחה.</translation>
 <translation id="5523149538118225875">{NUM_EXTENSIONS,plural, =1{מנהל המערכת שלך התקין תוסף}one{מנהל המערכת שלך התקין # תוספים}two{מנהל המערכת שלך התקין # תוספים}other{מנהל המערכת שלך התקין # תוספים}}</translation>
+<translation id="5523203511530504754">פתיחת קישורים של <ph name="PROTOCOL_SCHEME" /> ב-<ph name="APP_NAME" /></translation>
 <translation id="5523532775593636291">אתרים שמוסיפים לרשימה תמיד נשארים פעילים ושמורים בזיכרון</translation>
 <translation id="5523558474028191231">שם יכול לכלול אותיות, ספרות ותווים מיוחדים, והוא חייב להיות באורך של <ph name="MAX_CHARACTER_COUNT" /> תווים או פחות</translation>
 <translation id="5526745900034778153">צריך להיכנס מחדש כדי להמשיך את הסנכרון</translation>
@@ -7053,7 +7058,7 @@
 <translation id="5869522115854928033">סיסמאות שמורות</translation>
 <translation id="5870086504539785141">סגירת תפריט הנגישות</translation>
 <translation id="5870155679953074650">שגיאות חמורות</translation>
-<translation id="587234395549708302">‏לפנות את Bookmarks?</translation>
+<translation id="587234395549708302">למחוק את הסימניות?</translation>
 <translation id="5875534259258494936">שיתוף המסך הסתיים</translation>
 <translation id="5876851302954717356">כרטיסייה חדשה מצד ימין</translation>
 <translation id="5877064549588274448">הערוץ שונה. יש להפעיל מחדש את המכשיר להחלת השינויים.</translation>
@@ -7116,6 +7121,7 @@
 <translation id="5919305207330938913">‏השימוש בנתונים ייעשה בהתאם ל<ph name="BEGIN_LINK" />מדיניות הפרטיות של Google<ph name="END_LINK" />.</translation>
 <translation id="5919486630433391408">מנהל המערכת השבית את עדכון הקושחה.</translation>
 <translation id="5920543303088087579">החיבור לרשת הזו הושבת על ידי מנהל המערכת</translation>
+<translation id="5922493635960054020">‏חסימת הגישה לכלי האופטימיזציה של JavaScript באתרים שביקרת בהם לאחרונה. נדרשת גלישה בטוחה</translation>
 <translation id="5922963926582976524">ניתוק הרשת לשיתוף אינטרנט מיידי בין מכשירים</translation>
 <translation id="5924047253200400718">קבלת עזרה<ph name="SCANNING_STATUS" /></translation>
 <translation id="5924438086390153180">‏צריך לשאול לפני שמעתיקים או מעבירים קובצי Microsoft ל-Google Drive</translation>
@@ -7418,6 +7424,7 @@
 <translation id="6135826623269483856">אין הרשאה לנהל את החלונות בכל המסכים</translation>
 <translation id="6136285399872347291">מחיקת תו אחד</translation>
 <translation id="6136287496450963112">מפתח האבטחה שלך אינו מוגן באמצעות קוד אימות. כדי לנהל טביעות אצבע, יש ליצור תחילה קוד אימות.</translation>
+<translation id="6136637346277431354">הוספת כרטיסיות ועוד</translation>
 <translation id="6138680304137685902">‏חתימת X9.62 ECDSA עם SHA-384</translation>
 <translation id="6140948187512243695">הצג פרטים</translation>
 <translation id="6141988275892716286">אישור הורדה</translation>
@@ -9598,6 +9605,7 @@
 <translation id="7663859337051362114">‏הוספת פרופיל eSim</translation>
 <translation id="76641554187607347">אין מקלדת מחוברת</translation>
 <translation id="7664442418269614729">שימוש בכרטיס הזה באייפון</translation>
+<translation id="7664822217939981459">בחירת אפליקציה לפתיחת הקישור <ph name="PROTOCOL_SCHEME" /></translation>
 <translation id="7665082356120621510">הקצאת שטח קבוע</translation>
 <translation id="7665369617277396874">חשבון חדש</translation>
 <translation id="7665445336029073980">היסטוריית ההורדות המלאה</translation>
@@ -10932,7 +10940,7 @@
 <translation id="8561565784790166472">יש להמשיך בזהירות</translation>
 <translation id="8561853412914299728"><ph name="TAB_TITLE" /> <ph name="EMOJI_PLAYING" /></translation>
 <translation id="8562115322675481339">יצירה של קבוצת כרטיסיות חדשה</translation>
-<translation id="8563043098557365232">מותרת רק לשירותי מערכת</translation>
+<translation id="8563043098557365232">יש אישור רק לשירותי מערכת</translation>
 <translation id="8564220755011656606">אין גישה למיקרופון</translation>
 <translation id="8565021320663436446">להסיר את <ph name="MEMBER_FULL_NAME" />?</translation>
 <translation id="8565650234829130278">בוצע ניסיון לשדרג יישום לאחור.</translation>
@@ -11222,6 +11230,7 @@
 <translation id="8750786237117206586">‏ניהול הגדרות האודיו של ChromeOS Flex</translation>
 <translation id="8751034568832412184">בית ספרי</translation>
 <translation id="8751329102746373229">מהאדמין שלך</translation>
+<translation id="875242763254084885">האתר <ph name="ORIGIN" /> רוצה לפתוח את הקישור הזה באפליקציה.</translation>
 <translation id="8752451679755290210">מעבר אוטומטי בין פריטים</translation>
 <translation id="8753948258138515839">‏האפליקציה 'קבצים' נותנת גישה מהירה לקבצים ששמרת ב-Google Drive, בהתקן אחסון חיצוני או במכשיר שלך עם ChromeOS Flex.</translation>
 <translation id="8754200782896249056">‏&lt;p&gt;בעת הפעלת <ph name="PRODUCT_NAME" /> בסביבה של שולחנות עבודה נתמכים, ייעשה שימוש בהגדרות ה-Proxy של המערכת. עם זאת, ייתכן שהמערכת שלך אינה נתמכת, או שאירעה בעיה בהפעלת תצורת המערכת.&lt;/p&gt;
diff --git a/chrome/app/resources/generated_resources_ja.xtb b/chrome/app/resources/generated_resources_ja.xtb
index 74634ca..12197cf 100644
--- a/chrome/app/resources/generated_resources_ja.xtb
+++ b/chrome/app/resources/generated_resources_ja.xtb
@@ -9198,7 +9198,7 @@
 <translation id="7410344089573941623"><ph name="HOST" /> がカメラやマイクへのアクセスを要求しているか確認する</translation>
 <translation id="7410421966064092098">サイトは、あなたが bot でないことを確認する情報を提供できません。</translation>
 <translation id="7410852728357935715">デバイスにキャスト</translation>
-<translation id="741204030948306876">ON にする</translation>
+<translation id="741204030948306876">オンにする</translation>
 <translation id="7412226954991670867">GPU メモリ</translation>
 <translation id="741370456693729525">普段使う表現で閲覧履歴やアクセスしたサイトを検索します</translation>
 <translation id="7414464185801331860">18 倍</translation>
diff --git a/chrome/app/resources/generated_resources_ka.xtb b/chrome/app/resources/generated_resources_ka.xtb
index 8cf6da6a9..7197ab66 100644
--- a/chrome/app/resources/generated_resources_ka.xtb
+++ b/chrome/app/resources/generated_resources_ka.xtb
@@ -4932,6 +4932,7 @@
 <translation id="437809255587011096">ტექსტის სტილისტური გაფორმების გამოცხადება</translation>
 <translation id="4378154925671717803">ტელეფონი</translation>
 <translation id="4378308539633073595">წინ გადაადგილება</translation>
+<translation id="4378363201814870400">მართეთ თქვენი ავტომატური შევსების ინფორმაცია, როგორიცაა პაროლები, გადახდის მეთოდები, მისამართები და პირადობის დამადასტურებელი მოწმობები, რათა გააუმჯობესოთ დათვალიერების სიჩქარე</translation>
 <translation id="4378551569595875038">დაკავშირება…</translation>
 <translation id="4378556263712303865">მოწყობილობის მოთხოვნა</translation>
 <translation id="4378589147979399051">მართეთ თქვენი მაუსის კურსორი ძირითადი კლავიატურით</translation>
diff --git a/chrome/app/resources/generated_resources_km.xtb b/chrome/app/resources/generated_resources_km.xtb
index 2adcb199..f154b51 100644
--- a/chrome/app/resources/generated_resources_km.xtb
+++ b/chrome/app/resources/generated_resources_km.xtb
@@ -2885,6 +2885,7 @@
 <translation id="2950666755714083615">ចុះ​ឈ្មោះ​ខ្ញុំ</translation>
 <translation id="2953210795988451570">ការធ្វើបច្ចុប្បន្នភាពផ្នែកសុវត្ថិភាពបានដល់ទីបញ្ចប់ហើយ។ ប្ដូរទៅ Chromebook ថ្មី។</translation>
 <translation id="2953218713108551165">មិនអនុញ្ញាត​ការជូនដំណឹង​សម្រាប់ <ph name="SITE" /> ទេ។ អ្នក​នឹង​ត្រូវបាន​សួរ​ម្ដងទៀត​ នៅពេល​អ្នកចូលមើល​លើកក្រោយ។</translation>
+<translation id="2955048943983973430">សេវាកម្ម Google ដែល​ពាក់ព័ន្ធ</translation>
 <translation id="2956070239128776395">ផ្នែក​នៅក្នុង​ក្រុម៖ <ph name="ERROR_LINE" /></translation>
 <translation id="2957124229512318478">បើក​មុខងារអ្នកអភិវឌ្ឍន៍ ដើម្បីប្រើ​កម្មវិធីបន្ថែម​នេះដែល Chrome Web Store មិនអាច​ត្រួតពិនិត្យ​បានទេ។</translation>
 <translation id="2958721676848865875">ការព្រមាន​អំពី​ការខ្ចប់​កម្មវិធី​បន្ថែម</translation>
@@ -4639,6 +4640,7 @@
 <translation id="4151503145138736576">មិនមានទំហំផ្ទុកក្រៅអ៊ីនធឺណិតដែលត្រូវសម្អាតទេ</translation>
 <translation id="4152011295694446843">អ្នក​នឹងឃើញ​ចំណាំ​របស់អ្នក​នៅទីនេះ</translation>
 <translation id="4152670763139331043">{NUM_TABS,plural, =1{ផ្ទាំង 1}other{ផ្ទាំង #}}</translation>
+<translation id="4153858337812834972">ផ្ទាំង​ថ្មីៗបំផុត</translation>
 <translation id="4154658846204884961">ផ្កាយដុះកន្ទុយ</translation>
 <translation id="4154664944169082762">ស្នាមម្រាមដៃ</translation>
 <translation id="4156297721709710037"><ph name="WINDOW_TITLE" /> - ទិដ្ឋភាព​ខាងឆ្វេង</translation>
@@ -7113,6 +7115,7 @@
 <translation id="5919305207330938913">ទិន្នន័យ​ដែលបាន​ប្រមូល​ទាំងអស់​នឹងត្រូវបាន​ប្រើប្រាស់ដោយ​យោងតាម<ph name="BEGIN_LINK" />គោលការណ៍​ឯកជនភាព<ph name="END_LINK" />របស់ Google។</translation>
 <translation id="5919486630433391408">ការដំឡើង​កំណែ​កម្មវិធី​បង្កប់​ត្រូវបានបិទ​ដោយ​អ្នកគ្រប់គ្រង​របស់អ្នក។</translation>
 <translation id="5920543303088087579">ការ​តភ្ជាប់​ទៅ​បណ្តាញនេះ​ត្រូវបាន​បិទ​ដោយ​អ្នកគ្រប់គ្រង​របស់​អ្នក</translation>
+<translation id="5922493635960054020">ទប់ស្កាត់​ការចូលប្រើ​ឧបករណ៍​បង្កើន​ប្រសិទ្ធភាព JavaScript នៅលើ​គេហទំព័រ​ដែលបាន​ចូលមើលថ្មីៗ។ តម្រូវឱ្យប្រើ​ការរុករក​ដោយ​សុវត្ថិភាព</translation>
 <translation id="5922963926582976524">ផ្ដាច់បណ្ដាញហតស្ប៉តភ្លាមៗ</translation>
 <translation id="5924047253200400718">ទទួលជំនួយ<ph name="SCANNING_STATUS" /></translation>
 <translation id="5924438086390153180">ស្នើសុំ​មុនពេល​ចម្លង ឬ​ផ្លាស់ទី​ឯកសារ Microsoft ទៅ Google ថាស</translation>
@@ -7415,6 +7418,7 @@
 <translation id="6135826623269483856">មិនបាន​អនុញ្ញាតឱ្យគ្រប់គ្រងវិនដូនៅលើផ្ទាំងអេក្រង់ទាំងអស់របស់អ្នក​ទេ</translation>
 <translation id="6136285399872347291">លុបថយក្រោយ</translation>
 <translation id="6136287496450963112">សោសុវត្ថិភាពរបស់អ្នក​មិនបាន​ប្រើកូដ PIN សម្រាប់​ការពារទេ។ ដើម្បីគ្រប់គ្រងស្នាម​ម្រាមដៃ សូមបង្កើតកូដ PIN ជាមុនសិន។</translation>
+<translation id="6136637346277431354">បញ្ចូល​ផ្ទាំង និងអ្វីៗជា​ច្រើនទៀត</translation>
 <translation id="6138680304137685902">ហត្ថលេខា X9.62 ECDSA ជាមួយ SHA-384</translation>
 <translation id="6140948187512243695">បង្ហាញ​ព័ត៌មាន​លម្អិត</translation>
 <translation id="6141988275892716286">បញ្ជាក់​ការទាញយក</translation>
diff --git a/chrome/app/resources/generated_resources_lo.xtb b/chrome/app/resources/generated_resources_lo.xtb
index 2815e103..ea6e1ef0 100644
--- a/chrome/app/resources/generated_resources_lo.xtb
+++ b/chrome/app/resources/generated_resources_lo.xtb
@@ -2884,6 +2884,7 @@
 <translation id="2950666755714083615">ໃຫ້ຂ້ອຍລົງທະບຽນ</translation>
 <translation id="2953210795988451570">ການອັບເດດຄວາມປອດໄພໄດ້ສິ້ນສຸດລົງແລ້ວ. ອັບເກຣດເປັນ Chromebook ເວີຊັນໃໝ່.</translation>
 <translation id="2953218713108551165">ບໍ່ອະນຸຍາດການແຈ້ງເຕືອນສຳລັບ <ph name="SITE" />. ທ່ານຈະຖືກຖາມອີກເທື່ອໜຶ່ງໃນການເຂົ້າເບິ່ງເທື່ອຕໍ່ໄປຂອງທ່ານ.</translation>
+<translation id="2955048943983973430">ບໍລິການ Google ທີ່ກ່ຽວຂ້ອງ</translation>
 <translation id="2956070239128776395">ສ່ວນທີ່ຝັງຢູ່ໃນກຸ່ມ: <ph name="ERROR_LINE" /></translation>
 <translation id="2957124229512318478">ເປີດໂໝດຜູ້​ພັດ​ທະ​ນາ​ເພື່ອໃຊ້ສ່ວນຂະຫຍາຍນີ້, ເຊິ່ງບໍ່ສາມາດກວດສອບໄດ້ໂດຍ Chrome Web Store.</translation>
 <translation id="2958721676848865875">ຄຳເຕືອນກ່ຽວກັບສ່ວນຂະຫຍາຍແພັກ</translation>
@@ -4640,6 +4641,7 @@
 <translation id="4151503145138736576">ບໍ່ມີບ່ອນຈັດເກັບຂໍ້ມູນອອບລາຍໃຫ້ລຶບລ້າງ</translation>
 <translation id="4152011295694446843">ທ່ານຈະເຫັນບຸກມາກຂອງທ່ານຢູ່ບ່ອນນີ້</translation>
 <translation id="4152670763139331043">{NUM_TABS,plural, =1{1 ແຖບ}other{# ​ແຖບ}}</translation>
+<translation id="4153858337812834972">ແຖບຫຼ້າສຸດ</translation>
 <translation id="4154658846204884961">ດາວຫາງ</translation>
 <translation id="4154664944169082762">ລາຍນີ້ວມື</translation>
 <translation id="4156297721709710037"><ph name="WINDOW_TITLE" /> - ມຸມມອງທາງຊ້າຍ</translation>
@@ -7114,6 +7116,7 @@
 <translation id="5919305207330938913">ການໃຊ້ຂໍ້ມູນທັງໝົດທີ່ຮວບຮວມຈະເປັນໄປຕາມ <ph name="BEGIN_LINK" />ນະໂຍບາຍຄວາມເປັນສ່ວນຕົວ<ph name="END_LINK" /> ຂອງ Google.</translation>
 <translation id="5919486630433391408">ຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານປິດການນຳໃຊ້ການອັບເດດເຟີມແວ.</translation>
 <translation id="5920543303088087579">ການເຊື່ອມຕໍ່ກັບເຄືອຂ່າຍນີ້ຖືກປິດນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບ</translation>
+<translation id="5922493635960054020">ບລັອກການເຂົ້າເຖິງຕົວເພີ່ມປະສິດທິພາບ JavaScript ຢູ່ເວັບໄຊທີ່ຫາກໍເຂົ້າເບິ່ງ. ຕ້ອງໃຊ້ Safe Browsing</translation>
 <translation id="5922963926582976524">ຍົກເລີກການເຊື່ອມຕໍ່ເຄືອຂ່າຍຮັອດສະປອດພ້ອມໃຊ້</translation>
 <translation id="5924047253200400718">ຂໍຄວາມຊ່ວຍເຫຼືອ<ph name="SCANNING_STATUS" /></translation>
 <translation id="5924438086390153180">ສອບຖາມກ່ອນສຳເນົາ ຫຼື ຍ້າຍໄຟລ໌ Microsoft ໄປໃສ່ Google Drive</translation>
@@ -7416,6 +7419,7 @@
 <translation id="6135826623269483856">ບໍ່ອະນຸຍາດໃຫ້ຈັດການໜ້າຈໍຢູ່ການສະແດງຜົນທັງໝົດຂອງທ່ານ</translation>
 <translation id="6136285399872347291">ລຶບຖອຍຫຼັງ</translation>
 <translation id="6136287496450963112">ກະແຈຄວາມປອດໄພຂອງທ່ານບໍ່ໄດ້ຮັບການປົກປ້ອງດ້ວຍ PIN. ເພື່ອຈັດການລາຍນິ້ວມື, ໃຫ້ສ້າງ PIN ກ່ອນ.</translation>
+<translation id="6136637346277431354">ເພີ່ມແຖບ ແລະ ອື່ນໆ</translation>
 <translation id="6138680304137685902">ລາຍເຊັນ X9.62 ECDSA ທີ່ມີ SHA-384</translation>
 <translation id="6140948187512243695">ສະແດງລາຍລະອຽດ</translation>
 <translation id="6141988275892716286">ຢືນຢັນການດາວໂຫຼດ</translation>
diff --git a/chrome/app/resources/generated_resources_mn.xtb b/chrome/app/resources/generated_resources_mn.xtb
index ef682f1..154500a2 100644
--- a/chrome/app/resources/generated_resources_mn.xtb
+++ b/chrome/app/resources/generated_resources_mn.xtb
@@ -4941,6 +4941,7 @@
 <translation id="437809255587011096">Текст загварчлалыг зарлах</translation>
 <translation id="4378154925671717803">Утас</translation>
 <translation id="4378308539633073595">Урагш гүйлгэх</translation>
+<translation id="4378363201814870400">Илүү хурдан үзэхэд зориулж нууц үг, төлбөрийн хэрэгсэл, хаяг, биеийн байцаалт зэрэг автоматаар бөглөх мэдээллээ удирдана уу</translation>
 <translation id="4378551569595875038">Холбож байна...</translation>
 <translation id="4378556263712303865">Төхөөрөмжийн шаардлага</translation>
 <translation id="4378589147979399051">Үндсэн гараараа хулганын курсороо хянах</translation>
diff --git a/chrome/app/resources/generated_resources_ms.xtb b/chrome/app/resources/generated_resources_ms.xtb
index 4a6c2ae..ce623af 100644
--- a/chrome/app/resources/generated_resources_ms.xtb
+++ b/chrome/app/resources/generated_resources_ms.xtb
@@ -1001,6 +1001,7 @@
 <translation id="166278006618318542">Algoritma Kekunci Awam Subjek</translation>
 <translation id="1662801900924515589"><ph name="APP" /> dipasang</translation>
 <translation id="1663698992894057019">Tingkatkan kepada Chromebook baharu untuk mendapatkan kemaskinian keselamatan dan perisian terkini</translation>
+<translation id="1664497218584335032">Laman web mahu membuka pautan ini pada apl.</translation>
 <translation id="166454475160289583">Tidak dibenarkan untuk menggantikan input papan kekunci anda</translation>
 <translation id="1665328953287874063">Gunakan kata laluan atau PIN untuk membuka kunci <ph name="DEVICE_TYPE" /> anda</translation>
 <translation id="1665859804801131136">Ekspresionisme</translation>
@@ -6564,10 +6565,12 @@
 <translation id="5517304475148761050">Apl ini memerlukan akses kepada Gedung Play</translation>
 <translation id="5517412723934627386"><ph name="NETWORK_TYPE" /> - <ph name="NETWORK_DISPLAY_NAME" /></translation>
 <translation id="5519195206574732858">LTE</translation>
+<translation id="5519766046604311137">Sentiasa buka pautan <ph name="PROTOCOL_SCHEME" /> pada apl ini</translation>
 <translation id="5520605865175417976">Alih keluar tindakan <ph name="ACTION_TO_REMOVE" /></translation>
 <translation id="5521078259930077036">Adakah ini halaman utama yang anda jangkakan?</translation>
 <translation id="5522156646677899028">Sambungan ini mengandungi kelemahan keselamatan yang serius.</translation>
 <translation id="5523149538118225875">{NUM_EXTENSIONS,plural, =1{Satu sambungan telah dipasang oleh pentadbir anda}other{# sambungan telah dipasang oleh pentadbir anda}}</translation>
+<translation id="5523203511530504754">Buka pautan <ph name="PROTOCOL_SCHEME" /> pada <ph name="APP_NAME" /></translation>
 <translation id="5523532775593636291">Laman yang anda tambahkan akan sentiasa aktif dan memori tidak akan dikosongkan daripada laman tersebut</translation>
 <translation id="5523558474028191231">Nama boleh menggunakan huruf, nombor serta aksara khas dan mestilah tidak melebihi <ph name="MAX_CHARACTER_COUNT" /> aksara</translation>
 <translation id="5526745900034778153">Log masuk sekali lagi untuk menyambung semula penyegerakan</translation>
@@ -9598,6 +9601,7 @@
 <translation id="7663859337051362114">Tambah profil eSIM</translation>
 <translation id="76641554187607347">Tiada papan kekunci disambungkan</translation>
 <translation id="7664442418269614729">Gunakan kad ini pada iPhone anda</translation>
+<translation id="7664822217939981459">Pilih apl untuk membuka pautan <ph name="PROTOCOL_SCHEME" /></translation>
 <translation id="7665082356120621510">Saiz rizab</translation>
 <translation id="7665369617277396874">Tambah akaun</translation>
 <translation id="7665445336029073980">Sejarah muat turun penuh</translation>
@@ -11221,6 +11225,7 @@
 <translation id="8750786237117206586">Urus tetapan audio ChromeOS Flex</translation>
 <translation id="8751034568832412184">Sekolah</translation>
 <translation id="8751329102746373229">Daripada pentadbir anda</translation>
+<translation id="875242763254084885">Laman <ph name="ORIGIN" /> mahu membuka pautan ini pada apl.</translation>
 <translation id="8752451679755290210">Bergerak antara item secara automatik</translation>
 <translation id="8753948258138515839">Apl Fail memberikan akses pantas kepada fail yang telah anda simpan di Google Drive, storan luaran atau peranti Chrome OS Flex anda.</translation>
 <translation id="8754200782896249056">&lt;p&gt;Apabila menjalankan <ph name="PRODUCT_NAME" /> di bawah persekitaran desktop yang disokong, tetapan proksi sistem akan digunakan. Bagaimanapun, sama ada sistem anda tidak disokong atau terdapat masalah melancarkan konfigurasi sistem anda.&lt;/p&gt;
diff --git a/chrome/app/resources/generated_resources_th.xtb b/chrome/app/resources/generated_resources_th.xtb
index 1f39d21..c052874 100644
--- a/chrome/app/resources/generated_resources_th.xtb
+++ b/chrome/app/resources/generated_resources_th.xtb
@@ -2872,6 +2872,7 @@
 <translation id="2950666755714083615">ให้ฉันลงชื่อสมัครใช้</translation>
 <translation id="2953210795988451570">การอัปเดตความปลอดภัยสิ้นสุดแล้ว เปลี่ยนไปใช้ Chromebook รุ่นใหม่</translation>
 <translation id="2953218713108551165">ไม่อนุญาตให้ <ph name="SITE" /> ส่งการแจ้งเตือน ระบบจะถามคุณอีกครั้งเมื่อเข้าชมครั้งถัดไป</translation>
+<translation id="2955048943983973430">บริการของ Google ที่เกี่ยวข้อง</translation>
 <translation id="2956070239128776395">ส่วนที่ฝังอยู่ในกลุ่ม: <ph name="ERROR_LINE" /></translation>
 <translation id="2957124229512318478">เปิดโหมดนักพัฒนาซอฟต์แวร์เพื่อใช้ส่วนขยายนี้ ซึ่ง Chrome เว็บสโตร์ไม่สามารถตรวจสอบได้</translation>
 <translation id="2958721676848865875">คำเตือนเกี่ยวกับการรวมแพ็กเกจส่วนขยาย</translation>
@@ -4626,6 +4627,7 @@
 <translation id="4151503145138736576">ไม่มีพื้นที่เก็บข้อมูลออฟไลน์ให้ล้าง</translation>
 <translation id="4152011295694446843">คุณจะเห็นบุ๊กมาร์กที่นี่</translation>
 <translation id="4152670763139331043">{NUM_TABS,plural, =1{1 แท็บ}other{# แท็บ}}</translation>
+<translation id="4153858337812834972">แท็บล่าสุด</translation>
 <translation id="4154658846204884961">ดาวหาง</translation>
 <translation id="4154664944169082762">ลายนิ้วมือ</translation>
 <translation id="4156297721709710037"><ph name="WINDOW_TITLE" /> - มุมมองด้านซ้าย</translation>
@@ -7096,6 +7098,7 @@
 <translation id="5919305207330938913">การใช้ข้อมูลทั้งหมดที่เก็บรวบรวมจะเป็นไปตาม<ph name="BEGIN_LINK" />นโยบายความเป็นส่วนตัวของ Google<ph name="END_LINK" /></translation>
 <translation id="5919486630433391408">ผู้ดูแลระบบปิดใช้การอัปเดตเฟิร์มแวร์</translation>
 <translation id="5920543303088087579">ผู้ดูแลระบบปิดใช้การเชื่อมต่อเครือข่ายนี้</translation>
+<translation id="5922493635960054020">บล็อกการเข้าถึงเครื่องมือเพิ่มประสิทธิภาพ JavaScript ในเว็บไซต์ที่เพิ่งเข้าชม ต้องใช้ Google Safe Browsing</translation>
 <translation id="5922963926582976524">ยกเลิกการเชื่อมต่อเครือข่ายฮอตสปอตด่วน</translation>
 <translation id="5924047253200400718">รับความช่วยเหลือ<ph name="SCANNING_STATUS" /></translation>
 <translation id="5924438086390153180">ถามก่อนคัดลอกหรือย้ายไฟล์ Microsoft ไปยัง Google ไดรฟ์</translation>
@@ -7397,6 +7400,7 @@
 <translation id="6135826623269483856">ไม่อนุญาตให้จัดการหน้าต่างบนจอแสดงผลทั้งหมด</translation>
 <translation id="6136285399872347291">ลบถอยหลัง</translation>
 <translation id="6136287496450963112">คีย์ความปลอดภัยไม่มีการป้องกันด้วย PIN โปรดสร้าง PIN ก่อนเพื่อจัดการลายนิ้วมือ</translation>
+<translation id="6136637346277431354">เพิ่มแท็บและอื่นๆ</translation>
 <translation id="6138680304137685902">ลายเซ็น X9.62 ECDSA ที่มี SHA-384</translation>
 <translation id="6140948187512243695">แสดงรายละเอียด</translation>
 <translation id="6141988275892716286">ยืนยันการดาวน์โหลด</translation>
diff --git a/chrome/app/resources/generated_resources_ur.xtb b/chrome/app/resources/generated_resources_ur.xtb
index 03e3533d..eef4892 100644
--- a/chrome/app/resources/generated_resources_ur.xtb
+++ b/chrome/app/resources/generated_resources_ur.xtb
@@ -1000,6 +1000,7 @@
 <translation id="166278006618318542">موضوع عوامی کلید الگورتھم</translation>
 <translation id="1662801900924515589"><ph name="APP" /> انسٹال ہو گئی</translation>
 <translation id="1663698992894057019">‏تازہ ترین سیکیورٹی اور سافٹ ویئر کے لیے نئے Chromebook میں اپ گریڈ کریں</translation>
+<translation id="1664497218584335032">یہ ویب سائٹ اس لنک کو ایک ایپ میں کھولنا چاہتی ہے۔</translation>
 <translation id="166454475160289583">آپ کی کی بورڈ ان پٹ کو اوور رائڈ کرنے کی اجازت نہیں ہے</translation>
 <translation id="1665328953287874063">‏اپنا <ph name="DEVICE_TYPE" /> غیر مقفل کرنے کے لیے پاس ورڈ یا PIN کا استعمال کریں</translation>
 <translation id="1665859804801131136">اظہار پسندی</translation>
@@ -6545,10 +6546,12 @@
 <translation id="5517304475148761050">‏اس ایپ کو Play اسٹور تک رسائی درکار ہے</translation>
 <translation id="5517412723934627386"><ph name="NETWORK_TYPE" /> - <ph name="NETWORK_DISPLAY_NAME" /></translation>
 <translation id="5519195206574732858">LTE</translation>
+<translation id="5519766046604311137">ہمیشہ اس ایپ میں <ph name="PROTOCOL_SCHEME" /> لنکس کھولیں</translation>
 <translation id="5520605865175417976">کارروائی ہٹائیں <ph name="ACTION_TO_REMOVE" /></translation>
 <translation id="5521078259930077036">کیا یہی وہ ہوم صفحہ ہے جس کی آپ توقع کر رہے تھے؟</translation>
 <translation id="5522156646677899028">اس ایکسٹینشن میں ایک سنگین سیکیورٹی خطرہ شامل ہے۔</translation>
 <translation id="5523149538118225875">{NUM_EXTENSIONS,plural, =1{آپ کے منتظم نے ایک ایکسٹینشن انسٹال کر دی ہے}other{# ایکسٹینشنز انسٹال کر دی گئی ہیں}}</translation>
+<translation id="5523203511530504754">‫<ph name="APP_NAME" /> میں <ph name="PROTOCOL_SCHEME" /> لنکس کھولیں</translation>
 <translation id="5523532775593636291">آپ جو سائٹس شامل کرتے ہیں وہ ہمیشہ فعال رہیں گی اور ان سے میموری کو خالی نہیں کیا جائے گا</translation>
 <translation id="5523558474028191231">نام میں کلمات، نمبرز اور مخصوص حروف کا استعمال کیا جا سکتا ہے اور نام میں <ph name="MAX_CHARACTER_COUNT" /> یا اس سے کم حروف کا ہونا ضروری ہے</translation>
 <translation id="5526745900034778153">مطابقت پذیری دوبارہ شروع کرنے کیلئے دوبارہ سائن ان کریں</translation>
@@ -9576,6 +9579,7 @@
 <translation id="7663859337051362114">‏eSIM پروفائل شامل کریں</translation>
 <translation id="76641554187607347">کوئی کی بورڈ منسلک نہیں ہے</translation>
 <translation id="7664442418269614729">‏اس کارڈ کو اپنے iPhone پر استعمال کریں</translation>
+<translation id="7664822217939981459">‫<ph name="PROTOCOL_SCHEME" /> لنک کو کھولنے کیلئے کوئی ایپ منتخب کریں</translation>
 <translation id="7665082356120621510">سائز ریزرو کریں</translation>
 <translation id="7665369617277396874">اکاؤنٹ شامل کریں</translation>
 <translation id="7665445336029073980">ڈاؤن لوڈ کی مکمل سرگزشت</translation>
@@ -11197,6 +11201,7 @@
 <translation id="8750786237117206586">‏ChromeOS Flex آڈیو ترتیبات کا نظم کریں</translation>
 <translation id="8751034568832412184">اسکول</translation>
 <translation id="8751329102746373229">آپ کے منتظم کی جانب سے</translation>
+<translation id="875242763254084885">یہ سائٹ <ph name="ORIGIN" /> اس لنک کو ایک ایپ میں کھولنا چاہتی ہے۔</translation>
 <translation id="8752451679755290210">خودکار طور پر ایک آئٹم سے دوسرے آئٹم پر جائیں</translation>
 <translation id="8753948258138515839">‏فائلز ایپ ان فائلوں تک فوری رسائی فراہم کرتی ہے جنہیں آپ نے Google Drive، خارجی اسٹوریج یا اپنے ChromeOS Flex آلے پر محفوظ کیا ہے۔</translation>
 <translation id="8754200782896249056">‏&lt;p&gt;تعاون یافتہ ڈیسک ٹاپ ماحول کے تحت <ph name="PRODUCT_NAME" /> کو چلانے پر، سسٹم کی پراکسی ترتیبات استعمال کی جائیں گی۔  تاہم، یا تو آپ کا سسٹم تعاون یافتہ نہیں ہے یا آپ کے سسٹم کا کنفیگریشن شروع کرنے میں ایک مسئلہ پیش آ گیا۔&lt;/p&gt;
diff --git a/chrome/app/resources/generated_resources_vi.xtb b/chrome/app/resources/generated_resources_vi.xtb
index cd3aa63..f4da0e9 100644
--- a/chrome/app/resources/generated_resources_vi.xtb
+++ b/chrome/app/resources/generated_resources_vi.xtb
@@ -4945,6 +4945,7 @@
 <translation id="437809255587011096">Định kiểu văn bản thông báo</translation>
 <translation id="4378154925671717803">Điện thoại</translation>
 <translation id="4378308539633073595">Cuộn tiến</translation>
+<translation id="4378363201814870400">Quản lý thông tin tự động điền (như mật khẩu, phương thức thanh toán, địa chỉ và thẻ căn cước) để duyệt web nhanh hơn</translation>
 <translation id="4378551569595875038">Đang kết nối…</translation>
 <translation id="4378556263712303865">Yêu cầu thiết bị</translation>
 <translation id="4378589147979399051">Điều khiển con trỏ chuột bằng bàn phím chính</translation>
diff --git a/chrome/app/resources/generated_resources_zh-HK.xtb b/chrome/app/resources/generated_resources_zh-HK.xtb
index 1042b89..01f31cb 100644
--- a/chrome/app/resources/generated_resources_zh-HK.xtb
+++ b/chrome/app/resources/generated_resources_zh-HK.xtb
@@ -4939,6 +4939,7 @@
 <translation id="437809255587011096">朗讀文字樣式</translation>
 <translation id="4378154925671717803">電話號碼</translation>
 <translation id="4378308539633073595">向前捲動</translation>
+<translation id="4378363201814870400">管理自動填入資料 (例如密碼、付款方法、地址和身分證),加快瀏覽速度</translation>
 <translation id="4378551569595875038">連線中…</translation>
 <translation id="4378556263712303865">裝置申請</translation>
 <translation id="4378589147979399051">透過主要鍵盤控制滑鼠游標</translation>
diff --git a/chrome/app/resources/locale_settings.grd b/chrome/app/resources/locale_settings.grd
index 48d8e19..9d4dd46bb 100644
--- a/chrome/app/resources/locale_settings.grd
+++ b/chrome/app/resources/locale_settings.grd
@@ -185,16 +185,6 @@
         </message>
       </if>
 
-      <!-- Links from the Clear Browsing Data dialog. -->
-      <message name="IDS_SETTINGS_CLEAR_DATA_MYACTIVITY_URL_IN_DIALOG" translateable="false">
-        https://myactivity.google.com/myactivity/?utm_source=chrome_n
-      </message>
-
-      <!-- Links on the History page. -->
-      <message name="IDS_SETTINGS_CLEAR_DATA_MYACTIVITY_URL_IN_HISTORY" translateable="false">
-        https://myactivity.google.com/myactivity/?utm_source=chrome_h
-      </message>
-
     </messages>
   </release>
 </grit>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index e0dd6fa..2c6ed713 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -780,6 +780,9 @@
   <message name="IDS_SETTINGS_HOME_AND_WORK_ADDRESS_REMOVED_MESSAGE" desc="Hidden text that is read to screen readers to confirm that a home and work address was removed.">
     Address removed
   </message>
+  <message name="IDS_SETTINGS_NAME_EMAIL_ADDRESS_REMOVED_MESSAGE" desc="Hidden text that is read to screen readers to confirm that a name email address was removed.">
+    Info removed
+  </message>
 
   <!-- Default Browser Page -->
   <if expr="not is_chromeos">
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_NAME_EMAIL_ADDRESS_REMOVED_MESSAGE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_NAME_EMAIL_ADDRESS_REMOVED_MESSAGE.png.sha1
new file mode 100644
index 0000000..4bd0d96a
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_NAME_EMAIL_ADDRESS_REMOVED_MESSAGE.png.sha1
@@ -0,0 +1 @@
+6ecfb4439ef73948405165bbdb58b70d96f9d796
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 69cb48a..8101c85c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -460,6 +460,8 @@
     "enterprise/browser_management/browser_management_status_provider.h",
     "enterprise/browser_management/management_service_factory.cc",
     "enterprise/browser_management/management_service_factory.h",
+    "enterprise/chrome_browser_main_extra_parts_enterprise.cc",
+    "enterprise/chrome_browser_main_extra_parts_enterprise.h",
     "enterprise/connectors/analysis/analysis_service_settings.cc",
     "enterprise/connectors/analysis/analysis_service_settings.h",
     "enterprise/connectors/connectors_manager.cc",
@@ -1543,10 +1545,6 @@
     "tpcd/metadata/devtools_observer.h",
     "tpcd/metadata/manager_factory.cc",
     "tpcd/metadata/manager_factory.h",
-    "tpcd/support/origin_trial_service.cc",
-    "tpcd/support/origin_trial_service.h",
-    "tpcd/support/origin_trial_service_factory.cc",
-    "tpcd/support/origin_trial_service_factory.h",
     "tpcd/support/top_level_trial_service.cc",
     "tpcd/support/top_level_trial_service.h",
     "tpcd/support/top_level_trial_service_factory.cc",
@@ -3153,6 +3151,8 @@
       "optimization_guide/android/optimization_guide_bridge.h",
       "optimization_guide/android/optimization_guide_tab_url_provider_android.cc",
       "optimization_guide/android/optimization_guide_tab_url_provider_android.h",
+      "page_content_annotations/android/page_content_extraction_tab_model_observer_android.cc",
+      "page_content_annotations/android/page_content_extraction_tab_model_observer_android.h",
       "page_image_service/android/image_service_bridge.cc",
       "page_image_service/android/image_service_bridge.h",
       "page_info/about_this_site_controller_android.cc",
@@ -3431,7 +3431,6 @@
       "//chrome/browser/tab_group_sync:android",
       "//chrome/browser/task_manager/android",
       "//chrome/browser/touch_to_fill/autofill/android:public",
-      "//chrome/browser/touch_to_fill/autofill/one_time_tokens/android:public",
       "//chrome/browser/touch_to_fill/password_manager/no_passkeys/android:public",
       "//chrome/browser/touch_to_fill/password_manager/password_generation/android",
       "//chrome/browser/touch_to_fill/password_manager/password_generation/android:public",
@@ -4864,22 +4863,6 @@
       "apps/intent_helper/chromeos_disabled_apps_throttle.h",
       "browser_process_platform_part_ash.cc",
       "browser_process_platform_part_ash.h",
-      "certificate_provider/certificate_provider.h",
-      "certificate_provider/certificate_provider_service.cc",
-      "certificate_provider/certificate_provider_service.h",
-      "certificate_provider/certificate_provider_service_factory.cc",
-      "certificate_provider/certificate_provider_service_factory.h",
-      "certificate_provider/certificate_requests.cc",
-      "certificate_provider/certificate_requests.h",
-      "certificate_provider/pin_dialog_manager.cc",
-      "certificate_provider/pin_dialog_manager.h",
-      "certificate_provider/security_token_pin_dialog_host.h",
-      "certificate_provider/security_token_pin_dialog_host_popup_impl.cc",
-      "certificate_provider/security_token_pin_dialog_host_popup_impl.h",
-      "certificate_provider/sign_requests.cc",
-      "certificate_provider/sign_requests.h",
-      "certificate_provider/thread_safe_certificate_map.cc",
-      "certificate_provider/thread_safe_certificate_map.h",
       "chrome_browser_interface_binders_webui_parts_chromeos.cc",
       "component_updater/app_provisioning_component_installer.cc",
       "component_updater/app_provisioning_component_installer.h",
@@ -5535,6 +5518,8 @@
       "//chrome/browser/ash/usb",
       "//chrome/browser/ash/video_conference",
       "//chrome/browser/ash/wallpaper_handlers",
+      "//chrome/browser/certificate_provider",
+      "//chrome/browser/certificate_provider:impl",
       "//chrome/browser/chromeos/app_mode",
       "//chrome/browser/chromeos/arc",
       "//chrome/browser/chromeos/cros_apps",
@@ -6172,6 +6157,11 @@
       # TODO: crbug.com/395179599 - Remove once //chrome/browser/net has its own
       # build target.
       "//remoting/host/chromeos:browser_interop",
+
+      # TODO(crbug.com/353332589): Remove this circular dependency when:
+      # - c/b/profiles/profile_manager.h"
+      # gets componentized.
+      "//chrome/browser/certificate_provider:impl",
     ]
 
     if (is_chrome_branded) {
@@ -6962,8 +6952,6 @@
       "browser_switcher/browser_switcher_sitelist.h",
       "browser_switcher/ieem_sitelist_parser.cc",
       "browser_switcher/ieem_sitelist_parser.h",
-      "enterprise/chrome_browser_main_extra_parts_enterprise.cc",
-      "enterprise/chrome_browser_main_extra_parts_enterprise.h",
       "enterprise/connectors/device_trust/attestation/browser/attestation_switches.cc",
       "enterprise/connectors/device_trust/attestation/browser/attestation_switches.h",
       "enterprise/connectors/device_trust/attestation/browser/attester.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 02f7aed..6dab8b3 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3914,7 +3914,6 @@
 
 // UnoPhase2FollowUp flags.
 const char kFastFollowFeatures[] =
-    "UnoForAuto,"
     "UnoPhase2FollowUp";
 
 const FeatureEntry::Choice kReplaceSyncPromosWithSignInPromosChoices[] = {
@@ -10298,12 +10297,6 @@
      flag_descriptions::kTabStripMouseCloseResizeDelayDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kTabStripMouseCloseResizeDelay)},
 
-    {"tab-strip-transition-in-desktop-window",
-     flag_descriptions::kTabStripTransitionInDesktopWindowName,
-     flag_descriptions::kTabStripTransitionInDesktopWindowDescription,
-     kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kTabStripTransitionInDesktopWindow)},
-
     {"tab-switcher-group-suggestions-android",
      flag_descriptions::kTabSwitcherGroupSuggestionsAndroidName,
      flag_descriptions::kTabSwitcherGroupSuggestionsAndroidDescription,
@@ -10790,12 +10783,6 @@
      FEATURE_VALUE_TYPE(chromeos::features::kCloudGamingDevice)},
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
-    {"content-settings-partitioning",
-     flag_descriptions::kContentSettingsPartitioningName,
-     flag_descriptions::kContentSettingsPartitioningDescription, kOsAll,
-     FEATURE_VALUE_TYPE(
-         content_settings::features::kContentSettingsPartitioning)},
-
 #if BUILDFLAG(IS_ANDROID)
     {"use-fullscreen-insets-api",
      flag_descriptions::kFullscreenInsetsApiMigrationName,
@@ -11223,19 +11210,6 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_ANDROID)
-    {"autofill-sync-ewallet-accounts",
-     flag_descriptions::kAutofillSyncEwalletAccountsName,
-     flag_descriptions::kAutofillSyncEwalletAccountsDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(autofill::features::kAutofillSyncEwalletAccounts)},
-
-    {"ewallet-payments", flag_descriptions::kEwalletPaymentsName,
-     flag_descriptions::kEwalletPaymentsDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(payments::facilitated::kEwalletPayments)},
-
-    {"payment-link-detection", flag_descriptions::kPaymentLinkDetectionName,
-     flag_descriptions::kPaymentLinkDetectionDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(blink::features::kPaymentLinkDetection)},
-
     {"process-rank-policy-android",
      flag_descriptions::kProcessRankPolicyAndroidName,
      flag_descriptions::kProcessRankPolicyAndroidDescription, kOsAndroid,
@@ -11647,15 +11621,6 @@
      flag_descriptions::kAutofillEnableCardBenefitsIphDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(autofill::features::kAutofillEnableCardBenefitsIph)},
 
-#if BUILDFLAG(IS_ANDROID)
-    {"support-multiple-server-requests-for-pix-payments",
-     flag_descriptions::kSupportMultipleServerRequestsForPixPaymentsName,
-     flag_descriptions::kSupportMultipleServerRequestsForPixPaymentsDescription,
-     kOsAndroid,
-     FEATURE_VALUE_TYPE(
-         payments::facilitated::kSupportMultipleServerRequestsForPixPayments)},
-#endif  // BUILDFLAG(IS_ANDROID)
-
     {"autofill-enable-card-info-runtime-retrieval",
      flag_descriptions::kAutofillEnableCardInfoRuntimeRetrievalName,
      flag_descriptions::kAutofillEnableCardInfoRuntimeRetrievalDescription,
@@ -12551,12 +12516,6 @@
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) ||
         // BUILDFLAG(IS_CHROMEOS)
 
-#if !BUILDFLAG(IS_ANDROID)
-    {"pinned-tab-toast-on-close", flag_descriptions::kPinnedTabToastOnCloseName,
-     flag_descriptions::kPinnedTabToastOnCloseDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(toast_features::kPinnedTabToastOnClose)},
-#endif  // !BUILDFLAG(IS_ANDROID)
-
     {"discount-autofill", commerce::flag_descriptions::kDiscountAutofillName,
      commerce::flag_descriptions::kDiscountAutofillDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(commerce::kDiscountAutofill)},
diff --git a/chrome/browser/accessibility/phrase_segmentation/dependency_parser_model_loader.cc b/chrome/browser/accessibility/phrase_segmentation/dependency_parser_model_loader.cc
index 85a4c5c..3f73ef4 100644
--- a/chrome/browser/accessibility/phrase_segmentation/dependency_parser_model_loader.cc
+++ b/chrome/browser/accessibility/phrase_segmentation/dependency_parser_model_loader.cc
@@ -60,7 +60,7 @@
     : opt_guide_(opt_guide), background_task_runner_(background_task_runner) {
   opt_guide_->AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OPTIMIZATION_TARGET_PHRASE_SEGMENTATION,
-      /*model_metadata=*/std::nullopt, this);
+      /*model_metadata=*/std::nullopt, background_task_runner, this);
 }
 
 DependencyParserModelLoader::~DependencyParserModelLoader() {
diff --git a/chrome/browser/actor/browser_action_util.cc b/chrome/browser/actor/browser_action_util.cc
index 925843a..2093a1c 100644
--- a/chrome/browser/actor/browser_action_util.cc
+++ b/chrome/browser/actor/browser_action_util.cc
@@ -792,7 +792,9 @@
           (*action_result.result->execution_end_time - actions_start_time)
               .InMilliseconds());
     }
-    {
+    // Don't report a page stabilization time if the start and end
+    // are the same. Not every tool needs stabilization.
+    if (*action_result.result->execution_end_time != action_result.end_time) {
       apc::ActionsResult_LatencyInformation_LatencyStep* latency_step =
           latency_info->add_latency_steps();
       latency_step->mutable_page_stabilization()->set_action_index(i);
diff --git a/chrome/browser/actor/tools/click_tool_browsertest.cc b/chrome/browser/actor/tools/click_tool_browsertest.cc
index fada577..a174fdb 100644
--- a/chrome/browser/actor/tools/click_tool_browsertest.cc
+++ b/chrome/browser/actor/tools/click_tool_browsertest.cc
@@ -57,7 +57,8 @@
           ::features::kActorPaintStabilityMode.GetName(paint_stability_mode)},
          {::features::kActorGeneralPageStabilityMode.name,
           ::features::kActorGeneralPageStabilityMode.GetName(
-              general_page_stability_mode)}});
+              general_page_stability_mode)},
+         {features::kGlicActorClickDelay.name, "200ms"}});
   }
 
   ~ActorClickToolBrowserTest() override = default;
@@ -371,6 +372,30 @@
   EXPECT_TRUE(actor_task().GetTabs().contains(active_tab()->GetHandle()));
 }
 
+IN_PROC_BROWSER_TEST_P(ActorClickToolBrowserTest, ClickTool_Delay) {
+  const GURL url =
+      embedded_test_server()->GetURL("/actor/page_with_clickable_element.html");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+  std::optional<int> body_id = GetDOMNodeId(*main_frame(), "body");
+  ASSERT_TRUE(body_id);
+
+  std::unique_ptr<ToolRequest> action =
+      MakeClickRequest(*main_frame(), body_id.value());
+  ActResultFuture result;
+  actor_task().Act(ToRequestList(action), result.GetCallback());
+  ExpectOkResult(result);
+
+  const double mousedown_timestamp =
+      EvalJs(main_frame(), "mouse_event_timestamps[0]").ExtractDouble();
+  const double mouseup_timestamp =
+      EvalJs(main_frame(), "mouse_event_timestamps[1]").ExtractDouble();
+  const base::TimeDelta delta =
+      base::Milliseconds(mouseup_timestamp - mousedown_timestamp);
+
+  EXPECT_GE(delta, features::kGlicActorClickDelay.Get());
+}
+
 INSTANTIATE_TEST_SUITE_P(
     ,
     ActorClickToolBrowserTest,
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index c219239..c8b2b4a0 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -338,20 +338,6 @@
   session_tab_helper->SetWindowID(session_window_id_);
 }
 
-std::unique_ptr<content::WebContents> TabAndroid::SwapWebContents(
-    std::unique_ptr<content::WebContents> new_contents,
-    bool did_start_load,
-    bool did_finish_load) {
-  content::WebContents* old_contents = web_contents_.get();
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_TabImpl_swapWebContents(env, weak_java_tab_.get(env),
-                               new_contents->GetJavaWebContents(),
-                               did_start_load, did_finish_load);
-  DCHECK_EQ(web_contents_, new_contents);
-  new_contents.release();
-  return base::WrapUnique(old_contents);
-}
-
 bool TabAndroid::IsCustomTab() const {
   JNIEnv* env = base::android::AttachCurrentThread();
   return Java_TabImpl_isCustomTab(env, weak_java_tab_.get(env));
diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h
index 9eb902c..b4b4487a 100644
--- a/chrome/browser/android/tab_android.h
+++ b/chrome/browser/android/tab_android.h
@@ -157,11 +157,6 @@
 
   void SetWindowSessionID(SessionID window_id);
 
-  std::unique_ptr<content::WebContents> SwapWebContents(
-      std::unique_ptr<content::WebContents> new_contents,
-      bool did_start_load,
-      bool did_finish_load);
-
   bool IsCustomTab() const;
   bool IsHidden() const;
 
diff --git a/chrome/browser/android/tab_state_storage_service_factory.cc b/chrome/browser/android/tab_state_storage_service_factory.cc
index 488e03fd..e55a38f 100644
--- a/chrome/browser/android/tab_state_storage_service_factory.cc
+++ b/chrome/browser/android/tab_state_storage_service_factory.cc
@@ -53,7 +53,8 @@
   DCHECK(context);
 
   if (!base::FeatureList::IsEnabled(
-          chrome::android::kTabStorageSqlitePrototype)) {
+          chrome::android::kTabStorageSqlitePrototype) ||
+      !base::FeatureList::IsEnabled(chrome::android::kTabCollectionAndroid)) {
     return nullptr;
   }
 
diff --git a/chrome/browser/apps/app_service/notifications_browsertest.cc b/chrome/browser/apps/app_service/notifications_browsertest.cc
index d92bbc9..759d475 100644
--- a/chrome/browser/apps/app_service/notifications_browsertest.cc
+++ b/chrome/browser/apps/app_service/notifications_browsertest.cc
@@ -821,7 +821,8 @@
 
     ash::ArcNotificationsHostInitializer::Observer* observer =
         apps::ArcAppsFactory::GetInstance()->GetForProfile(profile());
-    observer->OnSetArcNotificationsInstance(arc_notification_manager_.get());
+    observer->OnArcNotificationManagerInitialized(
+        arc_notification_manager_.get());
   }
 
   void TearDownOnMainThread() override {
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps.cc b/chrome/browser/apps/app_service/publishers/arc_apps.cc
index 4b7241e2..6e34e5b 100644
--- a/chrome/browser/apps/app_service/publishers/arc_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/arc_apps.cc
@@ -1334,16 +1334,12 @@
   }
 }
 
-void ArcApps::OnSetArcNotificationsInstance(
+void ArcApps::OnArcNotificationManagerInitialized(
     ash::ArcNotificationManagerBase* arc_notification_manager) {
   DCHECK(arc_notification_manager);
   notification_observation_.Observe(arc_notification_manager);
 }
 
-// TODO(crbug.com/442761233): Remove this.
-void ArcApps::OnArcNotificationInitializerDestroyed(
-    ash::ArcNotificationsHostInitializer* initializer) {}
-
 void ArcApps::OnNotificationUpdated(const std::string& notification_id,
                                     const std::string& app_id) {
   if (app_id.empty()) {
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps.h b/chrome/browser/apps/app_service/publishers/arc_apps.h
index fc9b2b3..371cb6dd 100644
--- a/chrome/browser/apps/app_service/publishers/arc_apps.h
+++ b/chrome/browser/apps/app_service/publishers/arc_apps.h
@@ -179,10 +179,8 @@
       arc::mojom::SupportedLinkChangeSource source) override;
 
   // ash::ArcNotificationsHostInitializer::Observer overrides.
-  void OnSetArcNotificationsInstance(
+  void OnArcNotificationManagerInitialized(
       ash::ArcNotificationManagerBase* arc_notification_manager) override;
-  void OnArcNotificationInitializerDestroyed(
-      ash::ArcNotificationsHostInitializer* initializer) override;
 
   // ArcNotificationManagerBase::Observer overrides.
   void OnNotificationUpdated(const std::string& notification_id,
diff --git a/chrome/browser/ash/arc/auth/BUILD.gn b/chrome/browser/ash/arc/auth/BUILD.gn
index 4ec2e97..528f26a 100644
--- a/chrome/browser/ash/arc/auth/BUILD.gn
+++ b/chrome/browser/ash/arc/auth/BUILD.gn
@@ -72,6 +72,7 @@
     "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/test:test_support",
+    "//chrome/browser/certificate_provider",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/signin",
     "//chrome/browser/ui",
diff --git a/chrome/browser/ash/arc/enterprise/cert_store/BUILD.gn b/chrome/browser/ash/arc/enterprise/cert_store/BUILD.gn
index d6a9698..68030a9 100644
--- a/chrome/browser/ash/arc/enterprise/cert_store/BUILD.gn
+++ b/chrome/browser/ash/arc/enterprise/cert_store/BUILD.gn
@@ -25,6 +25,7 @@
     "//chrome/browser/ash/platform_keys",
     "//chrome/browser/ash/platform_keys/key_permissions",
     "//chrome/browser/ash/policy/remote_commands",
+    "//chrome/browser/certificate_provider",
     "//chrome/browser/profiles:profile",
     "//chrome/common",
     "//chromeos/ash/components/platform_keys",
diff --git a/chrome/browser/ash/arc/session/BUILD.gn b/chrome/browser/ash/arc/session/BUILD.gn
index 54d8b8d..0b70e68 100644
--- a/chrome/browser/ash/arc/session/BUILD.gn
+++ b/chrome/browser/ash/arc/session/BUILD.gn
@@ -225,6 +225,7 @@
     "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/ash/policy/test_support",
     "//chrome/browser/ash/test:test_support",
+    "//chrome/browser/certificate_provider",
     "//chrome/browser/extensions",
     "//chrome/browser/profiles:profile",
     "//chrome/test:test_support",
diff --git a/chrome/browser/ash/dbus/BUILD.gn b/chrome/browser/ash/dbus/BUILD.gn
index e9218513..3a1eb64 100644
--- a/chrome/browser/ash/dbus/BUILD.gn
+++ b/chrome/browser/ash/dbus/BUILD.gn
@@ -91,6 +91,7 @@
     "//chrome/browser/ash/printing",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/settings",
+    "//chrome/browser/certificate_provider",
     "//chrome/browser/chromeos/policy/dlp",
     "//chrome/browser/profiles:profile",
     "//chrome/common:chrome_features",
@@ -303,6 +304,8 @@
     "//chrome/browser",
     "//chrome/browser/ash/login:test_support",
     "//chrome/browser/ash/net",
+    "//chrome/browser/certificate_provider",
+    "//chrome/browser/certificate_provider:test_support",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
     "//chrome/common:non_code_constants",
diff --git a/chrome/browser/ash/login/BUILD.gn b/chrome/browser/ash/login/BUILD.gn
index 349fd6ff..01fc0294 100644
--- a/chrome/browser/ash/login/BUILD.gn
+++ b/chrome/browser/ash/login/BUILD.gn
@@ -97,7 +97,6 @@
     "//chrome/browser/ash/login/saml",
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/policy/enrollment",
-    "//chrome/browser/ash/policy/enrollment",
     "//chrome/browser/profiles:profile",
     "//chromeos/ash/components/dbus/session_manager",
     "//chromeos/ash/components/demo_mode",
@@ -162,6 +161,7 @@
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/settings",
     "//chrome/browser/ash/system",
+    "//chrome/browser/certificate_provider",
     "//chrome/browser/password_manager/factories",
     "//chrome/browser/profiles",
     "//chrome/browser/signin",
@@ -439,6 +439,8 @@
     "//chrome/browser/ash/policy/test_support",
     "//chrome/browser/ash/settings",
     "//chrome/browser/ash/settings:test_support",
+    "//chrome/browser/certificate_provider",
+    "//chrome/browser/certificate_provider:test_support",
     "//chrome/browser/extensions",
     "//chrome/browser/lifetime:termination_notification",
     "//chrome/browser/metrics/structured:test_support",
diff --git a/chrome/browser/ash/login/demo_mode/BUILD.gn b/chrome/browser/ash/login/demo_mode/BUILD.gn
index b6e9bf9..196207b 100644
--- a/chrome/browser/ash/login/demo_mode/BUILD.gn
+++ b/chrome/browser/ash/login/demo_mode/BUILD.gn
@@ -109,6 +109,7 @@
     "//chromeos/ash/components/demo_mode",
     "//chromeos/ash/components/growth",
     "//chromeos/ash/components/system/",
+    "//components/language/core/browser",
   ]
 
   allow_circular_includes_from = [ "//chrome/browser/ash/login" ]
diff --git a/chrome/browser/ash/login/demo_mode/demo_login_controller.cc b/chrome/browser/ash/login/demo_mode/demo_login_controller.cc
index f2dfd31..2e32a00 100644
--- a/chrome/browser/ash/login/demo_mode/demo_login_controller.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_login_controller.cc
@@ -34,6 +34,7 @@
 #include "chromeos/ash/components/settings/user_login_permission_tracker.h"
 #include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/account_id/account_id.h"
+#include "components/language/core/browser/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_type.h"
@@ -74,6 +75,10 @@
 const char kDMToken[] = "dm_token";
 const char kClientID[] = "client_id";
 
+const char kDeviceInfo[] = "device_info";
+const char kLocale[] = "locale";
+const char kCountry[] = "country";
+
 // Maximum accepted size of an ItemSuggest response. 1MB.
 constexpr int kMaxResponseSize = 1024 * 1024;
 
@@ -346,6 +351,20 @@
   return policy_connector_ash->GetDeviceCloudPolicyManager();
 }
 
+base::Value::Dict GetDeviceInfo() {
+  // This field "locale" is used to set the language of the demo account.
+  const std::string& locale = g_browser_process->local_state()->GetString(
+      language::prefs::kApplicationLocale);
+  // This field "country" is intended to be used to control region specific
+  // behaviors, including TOS agreement, focus backend services and etc. Convert
+  // it to uppercase since some devices may still have the country in lowercase.
+  const std::string country = base::ToUpperASCII(
+      g_browser_process->local_state()->GetString(prefs::kDemoModeCountry));
+
+  // TODO(crbug.com/449237585): Add other DeviceInfo fields.
+  return base::Value::Dict().Set(kLocale, locale).Set(kCountry, country);
+}
+
 }  // namespace
 
 DemoLoginController::DemoLoginController(
@@ -445,6 +464,12 @@
 
   auto post_data = base::Value::Dict().Set(
       kDeviceIdentifier, std::move(device_identifier.value()));
+
+  if (features::IsSendDeviceInfoToDemoServerEnabled()) {
+    base::Value::Dict device_info = GetDeviceInfo();
+    post_data.Set(kDeviceInfo, std::move(device_info));
+  }
+
   url_loader_ =
       CreateDemoAccountURLLoader(GetDemoAccountUrl(kSetupDemoAccountEndpoint),
                                  kSetupAccountTrafficAnnotation);
diff --git a/chrome/browser/ash/login/lock/BUILD.gn b/chrome/browser/ash/login/lock/BUILD.gn
index 0445ae0..ff92aec 100644
--- a/chrome/browser/ash/login/lock/BUILD.gn
+++ b/chrome/browser/ash/login/lock/BUILD.gn
@@ -46,6 +46,7 @@
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/system",
+    "//chrome/browser/certificate_provider",
     "//chrome/browser/extensions",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui/ash/login",
@@ -181,6 +182,7 @@
     "//chrome/browser",
     "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/ash/settings:test_support",
+    "//chrome/browser/certificate_provider",
     "//chrome/browser/ui/ash/assistant",
     "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/session",
diff --git a/chrome/browser/ash/login/saml/BUILD.gn b/chrome/browser/ash/login/saml/BUILD.gn
index a4ac7df..0e96b06 100644
--- a/chrome/browser/ash/login/saml/BUILD.gn
+++ b/chrome/browser/ash/login/saml/BUILD.gn
@@ -90,6 +90,7 @@
 
   public_deps = [
     "//base",
+    "//chrome/browser/certificate_provider:test_support",
     "//chrome/test:test_support",
     "//chrome/test:test_support_ui",
     "//chromeos/ash/components/proximity_auth",
diff --git a/chrome/browser/ash/login/screens/sync_consent_screen.cc b/chrome/browser/ash/login/screens/sync_consent_screen.cc
index ca9f3760..3b3a84c 100644
--- a/chrome/browser/ash/login/screens/sync_consent_screen.cc
+++ b/chrome/browser/ash/login/screens/sync_consent_screen.cc
@@ -261,6 +261,10 @@
   UpdateScreen(*context());
 }
 
+void SyncConsentScreen::OnSyncShutdown(syncer::SyncService* sync) {
+  sync_service_observation_.Reset();
+}
+
 void SyncConsentScreen::MaybeEnableSyncForSkip() {
   // "sync everything" toggle is disabled during SyncService creation. We need
   // to turn it on if sync service needs to be enabled.
diff --git a/chrome/browser/ash/login/screens/sync_consent_screen.h b/chrome/browser/ash/login/screens/sync_consent_screen.h
index 78b6d5d..94b27352 100644
--- a/chrome/browser/ash/login/screens/sync_consent_screen.h
+++ b/chrome/browser/ash/login/screens/sync_consent_screen.h
@@ -105,6 +105,7 @@
 
   // syncer::SyncServiceObserver:
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
   // Reacts to user action on sync.
   void OnContinue(const bool opted_in,
diff --git a/chrome/browser/ash/net/BUILD.gn b/chrome/browser/ash/net/BUILD.gn
index 337561e..3278643a 100644
--- a/chrome/browser/ash/net/BUILD.gn
+++ b/chrome/browser/ash/net/BUILD.gn
@@ -55,6 +55,7 @@
     "//chrome/browser/ash/policy/handlers",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/system",
+    "//chrome/browser/certificate_provider",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/sync",
     "//chrome/browser/ui/login",
@@ -140,6 +141,7 @@
     "//chrome/browser/ash/net/network_diagnostics:unit_tests",
     "//chrome/browser/ash/net/rollback_network_config:unit_tests",
     "//chrome/browser/ash/settings:test_support",
+    "//chrome/browser/certificate_provider",
     "//chrome/browser/prefs",
     "//chrome/test:test_support",
     "//chromeos/ash/components/dbus",
diff --git a/chrome/browser/ash/platform_keys/BUILD.gn b/chrome/browser/ash/platform_keys/BUILD.gn
index 835c33ab..37bdef9 100644
--- a/chrome/browser/ash/platform_keys/BUILD.gn
+++ b/chrome/browser/ash/platform_keys/BUILD.gn
@@ -22,6 +22,7 @@
     "//base",
     "//chrome/browser/ash/kcer",
     "//chrome/browser/ash/profiles",
+    "//chrome/browser/certificate_provider",
     "//chrome/browser/profiles:profile",
     "//chromeos/ash/components/chaps_util",
     "//chromeos/ash/components/kcer",
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_events_observer_browsertest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_events_observer_browsertest.cc
index 2db234ef..490831cd0 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_events_observer_browsertest.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_events_observer_browsertest.cc
@@ -8,6 +8,8 @@
 
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
+#include "base/functional/callback_helpers.h"
+#include "base/run_loop.h"
 #include "base/values.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_ash.h"
@@ -232,7 +234,15 @@
   const auto app_id = InstallStandaloneWebApp(GURL(kWebAppUrl));
   ::chromeos::MissiveClientTestObserver missive_observer(
       base::BindRepeating(&IsMetricEventOfType, MetricEventType::APP_LAUNCHED));
-  ::web_app::LaunchWebAppBrowser(profile(), app_id);
+
+  base::RunLoop run_loop;
+  apps::AppServiceProxyFactory::GetForProfile(profile())->LaunchAppWithParams(
+      apps::AppLaunchParams(
+          app_id, apps::LaunchContainer::kLaunchContainerWindow,
+          WindowOpenDisposition::CURRENT_TAB, apps::LaunchSource::kFromTest),
+      base::IgnoreArgs<apps::LaunchResult&&>(run_loop.QuitClosure()));
+  run_loop.Run();
+
   const auto [priority, record] = missive_observer.GetNextEnqueuedRecord();
   AssertRecordData(priority, record);
   MetricData metric_data;
diff --git a/chrome/browser/ash/printing/usb_printer_util.cc b/chrome/browser/ash/printing/usb_printer_util.cc
index d34080b..1f300c9 100644
--- a/chrome/browser/ash/printing/usb_printer_util.cc
+++ b/chrome/browser/ash/printing/usb_printer_util.cc
@@ -412,6 +412,9 @@
   entry->printer.SetUri(UsbPrinterUri(device_info));
   entry->printer.set_id(CreateUsbPrinterId(device_info));
   entry->printer.set_supports_ippusb(UsbDeviceSupportsIppusb(device_info));
+  entry->printer.set_usb_device_id(chromeos::Printer::UsbDeviceId(
+      device_info.vendor_id, device_info.product_id));
+
   return true;
 }
 
diff --git a/chrome/browser/ash/printing/usb_printer_util_unittest.cc b/chrome/browser/ash/printing/usb_printer_util_unittest.cc
index f7d93d6..7ef1ba4 100644
--- a/chrome/browser/ash/printing/usb_printer_util_unittest.cc
+++ b/chrome/browser/ash/printing/usb_printer_util_unittest.cc
@@ -56,6 +56,17 @@
   EXPECT_THAT(entry.printer.uri().GetNormalized(), HasSubstr("?serial=?"));
 }
 
+TEST(UsbPrinterUtilTest, UsbDeviceToPrinter_UsbDeviceId) {
+  UsbDeviceInfo device_info;
+  device_info.vendor_id = 1;
+  device_info.product_id = 2;
+
+  PrinterDetector::DetectedPrinter entry;
+  ASSERT_TRUE(UsbDeviceToPrinter(device_info, &entry));
+  EXPECT_THAT(entry.printer.usb_device_id(),
+              chromeos::Printer::UsbDeviceId(1, 2));
+}
+
 TEST(UsbPrinterUtilTest, GuessEffectiveMakeAndModelDuplicatedManufacturer) {
   UsbDeviceInfo device_info;
   device_info.manufacturer_name = u"bixolon";
diff --git a/chrome/browser/ash/sync/sync_appsync_optin_client.cc b/chrome/browser/ash/sync/sync_appsync_optin_client.cc
index beedd3c5..d210060 100644
--- a/chrome/browser/ash/sync/sync_appsync_optin_client.cc
+++ b/chrome/browser/ash/sync/sync_appsync_optin_client.cc
@@ -158,4 +158,10 @@
   }
 }
 
+void SyncAppsyncOptinClient::OnSyncShutdown(syncer::SyncService* sync_service) {
+  // Unreachable, since `this` must be destroyed before SyncService shutdown,
+  // per precondition specified in the constructor.
+  NOTREACHED();
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ash/sync/sync_appsync_optin_client.h b/chrome/browser/ash/sync/sync_appsync_optin_client.h
index e059f6ecd..c289ae9 100644
--- a/chrome/browser/ash/sync/sync_appsync_optin_client.h
+++ b/chrome/browser/ash/sync/sync_appsync_optin_client.h
@@ -54,6 +54,7 @@
 
   // syncer::SyncServiceObserver
   void OnStateChanged(syncer::SyncService* sync_service) override;
+  void OnSyncShutdown(syncer::SyncService* sync_service) override;
 
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
@@ -91,4 +92,4 @@
 };
 }  // namespace ash
 
-#endif  //  CHROME_BROWSER_ASH_SYNC_SYNC_APPSYNC_OPTIN_CLIENT_H_
+#endif  // CHROME_BROWSER_ASH_SYNC_SYNC_APPSYNC_OPTIN_CLIENT_H_
diff --git a/chrome/browser/ash/video_conference/video_conference_app_service_client.cc b/chrome/browser/ash/video_conference/video_conference_app_service_client.cc
index f84c759..099a17a 100644
--- a/chrome/browser/ash/video_conference/video_conference_app_service_client.cc
+++ b/chrome/browser/ash/video_conference/video_conference_app_service_client.cc
@@ -152,14 +152,14 @@
   // This will be an AnchoredNudge, which is only visible if the tray is
   // visible; so we have to call this after HandleMediaUsageUpdate.
   if (update.CameraChanged() && is_capturing_camera &&
-      camera_system_disabled_) {
+      !camera_system_enabled_) {
     video_conference_manager_ash_->NotifyDeviceUsedWhileDisabled(
         crosapi::mojom::VideoConferenceMediaDevice::kCamera,
         base::UTF8ToUTF16(app_name), base::DoNothingAs<void(bool)>());
   }
 
   if (update.MicrophoneChanged() && is_capturing_microphone &&
-      microphone_system_disabled_) {
+      !microphone_system_enabled_) {
     video_conference_manager_ash_->NotifyDeviceUsedWhileDisabled(
         crosapi::mojom::VideoConferenceMediaDevice::kMicrophone,
         base::UTF8ToUTF16(app_name), base::DoNothingAs<void(bool)>());
diff --git a/chrome/browser/ash/video_conference/video_conference_app_service_client_browsertest.cc b/chrome/browser/ash/video_conference/video_conference_app_service_client_browsertest.cc
index 73a3dd5..eef8c43 100644
--- a/chrome/browser/ash/video_conference/video_conference_app_service_client_browsertest.cc
+++ b/chrome/browser/ash/video_conference/video_conference_app_service_client_browsertest.cc
@@ -658,10 +658,10 @@
   // video_conference_manager_ash.
   ash::VideoConferenceManagerAsh::Get()->SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice::kCamera,
-      /*disabled=*/true);
+      /*enabled=*/false);
   ash::VideoConferenceManagerAsh::Get()->SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice::kMicrophone,
-      /*disabled=*/true);
+      /*enabled=*/false);
 
   FakeVideoConferenceTrayController* fake_try_controller =
       static_cast<FakeVideoConferenceTrayController*>(
@@ -705,10 +705,10 @@
   // video_conference_manager_ash.
   ash::VideoConferenceManagerAsh::Get()->SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice::kCamera,
-      /*disabled=*/false);
+      /*enabled=*/true);
   ash::VideoConferenceManagerAsh::Get()->SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice::kMicrophone,
-      /*disabled=*/false);
+      /*enabled=*/true);
 
   // Accessing camera should not trigger NotifyDeviceUsedWhileDisabled because
   // camera is not disabled.
diff --git a/chrome/browser/ash/video_conference/video_conference_ash_feature_client.cc b/chrome/browser/ash/video_conference/video_conference_ash_feature_client.cc
index 873a437..d282e90 100644
--- a/chrome/browser/ash/video_conference/video_conference_ash_feature_client.cc
+++ b/chrome/browser/ash/video_conference/video_conference_ash_feature_client.cc
@@ -111,14 +111,14 @@
   // This will be an AnchoredNudge, which is only visible if the tray is
   // visible; so we have to call this after HandleMediaUsageUpdate.
   if (device_type == VmCameraMicManager::DeviceType::kCamera && is_capturing &&
-      camera_system_disabled_) {
+      !camera_system_enabled_) {
     video_conference_manager_ash_->NotifyDeviceUsedWhileDisabled(
         crosapi::mojom::VideoConferenceMediaDevice::kCamera,
         base::UTF8ToUTF16(app_name), base::DoNothingAs<void(bool)>());
   }
 
   if (device_type == VmCameraMicManager::DeviceType::kMic && is_capturing &&
-      microphone_system_disabled_) {
+      !microphone_system_enabled_) {
     video_conference_manager_ash_->NotifyDeviceUsedWhileDisabled(
         crosapi::mojom::VideoConferenceMediaDevice::kMicrophone,
         base::UTF8ToUTF16(app_name), base::DoNothingAs<void(bool)>());
diff --git a/chrome/browser/ash/video_conference/video_conference_ash_feature_client_browsertest.cc b/chrome/browser/ash/video_conference/video_conference_ash_feature_client_browsertest.cc
index 322964f..2abf827 100644
--- a/chrome/browser/ash/video_conference/video_conference_ash_feature_client_browsertest.cc
+++ b/chrome/browser/ash/video_conference/video_conference_ash_feature_client_browsertest.cc
@@ -235,10 +235,10 @@
   // video_conference_manager_ash.
   ash::VideoConferenceManagerAsh::Get()->SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice::kCamera,
-      /*disabled=*/true);
+      /*enabled=*/false);
   ash::VideoConferenceManagerAsh::Get()->SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice::kMicrophone,
-      /*disabled=*/true);
+      /*enabled=*/false);
 
   FakeVideoConferenceTrayController* fake_try_controller =
       static_cast<FakeVideoConferenceTrayController*>(
diff --git a/chrome/browser/ash/video_conference/video_conference_client_base.cc b/chrome/browser/ash/video_conference/video_conference_client_base.cc
index ab67aee..6d0f3d79 100644
--- a/chrome/browser/ash/video_conference/video_conference_client_base.cc
+++ b/chrome/browser/ash/video_conference/video_conference_client_base.cc
@@ -80,15 +80,15 @@
 }
 void VideoConferenceClientBase::SetSystemMediaDeviceStatus(
     crosapi::mojom::VideoConferenceMediaDevice device,
-    bool disabled,
+    bool enabled,
     SetSystemMediaDeviceStatusCallback callback) {
   switch (device) {
     case crosapi::mojom::VideoConferenceMediaDevice::kCamera:
-      camera_system_disabled_ = disabled;
+      camera_system_enabled_ = enabled;
       std::move(callback).Run(true);
       return;
     case crosapi::mojom::VideoConferenceMediaDevice::kMicrophone:
-      microphone_system_disabled_ = disabled;
+      microphone_system_enabled_ = enabled;
       std::move(callback).Run(true);
       return;
     case crosapi::mojom::VideoConferenceMediaDevice::kUnusedDefault:
diff --git a/chrome/browser/ash/video_conference/video_conference_client_base.h b/chrome/browser/ash/video_conference/video_conference_client_base.h
index 8072fcd..ce5ab4d 100644
--- a/chrome/browser/ash/video_conference/video_conference_client_base.h
+++ b/chrome/browser/ash/video_conference/video_conference_client_base.h
@@ -44,7 +44,7 @@
   void GetMediaApps(GetMediaAppsCallback callback) override;
   void SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice device,
-      bool disabled,
+      bool enabled,
       SetSystemMediaDeviceStatusCallback callback) override;
   void StopAllScreenShare() override;
 
@@ -63,8 +63,8 @@
   // to distinguish between different kinds of applications.
   virtual apps::AppType GetAppType(const AppIdString& app_id) = 0;
 
-  bool camera_system_disabled_{false};
-  bool microphone_system_disabled_{false};
+  bool camera_system_enabled_{true};
+  bool microphone_system_enabled_{true};
 
   // This records a list of AppState; each represents a video conference app.
   std::map<AppIdString, AppState> id_to_app_state_;
diff --git a/chrome/browser/ash/video_conference/video_conference_client_wrapper.cc b/chrome/browser/ash/video_conference/video_conference_client_wrapper.cc
index b521ca4c..738414f 100644
--- a/chrome/browser/ash/video_conference/video_conference_client_wrapper.cc
+++ b/chrome/browser/ash/video_conference/video_conference_client_wrapper.cc
@@ -36,11 +36,10 @@
 
 void VideoConferenceClientWrapper::SetSystemMediaDeviceStatus(
     crosapi::mojom::VideoConferenceMediaDevice device,
-    bool disabled,
+    bool enabled,
     crosapi::mojom::VideoConferenceManagerClient::
         SetSystemMediaDeviceStatusCallback callback) {
-  cpp_client_->SetSystemMediaDeviceStatus(device, disabled,
-                                          std::move(callback));
+  cpp_client_->SetSystemMediaDeviceStatus(device, enabled, std::move(callback));
 }
 
 void VideoConferenceClientWrapper::StopAllScreenShare() {
diff --git a/chrome/browser/ash/video_conference/video_conference_manager_ash.cc b/chrome/browser/ash/video_conference/video_conference_manager_ash.cc
index 53067e63..a811eaad 100644
--- a/chrome/browser/ash/video_conference/video_conference_manager_ash.cc
+++ b/chrome/browser/ash/video_conference/video_conference_manager_ash.cc
@@ -106,10 +106,10 @@
 
 void VideoConferenceManagerAsh::SetSystemMediaDeviceStatus(
     crosapi::mojom::VideoConferenceMediaDevice device,
-    bool disabled) {
+    bool enabled) {
   for (auto& [_, client_wrapper] : client_id_to_wrapper_) {
     client_wrapper.SetSystemMediaDeviceStatus(
-        device, disabled, base::BindOnce([](bool success) {
+        device, enabled, base::BindOnce([](bool success) {
           if (!success) {
             LOG(ERROR)
                 << "VideoConferenceClient::SetSystemMediaDeviceStatus was "
diff --git a/chrome/browser/ash/video_conference/video_conference_manager_ash.h b/chrome/browser/ash/video_conference/video_conference_manager_ash.h
index 8261493..7c3c8cb 100644
--- a/chrome/browser/ash/video_conference/video_conference_manager_ash.h
+++ b/chrome/browser/ash/video_conference/video_conference_manager_ash.h
@@ -48,7 +48,7 @@
   void ReturnToApp(const base::UnguessableToken& id) override;
   void SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice device,
-      bool disabled) override;
+      bool enabled) override;
   void StopAllScreenShare() override;
   void CreateBackgroundImage() override;
 
diff --git a/chrome/browser/autofill/BUILD.gn b/chrome/browser/autofill/BUILD.gn
index d95704d0..2d401e0b 100644
--- a/chrome/browser/autofill/BUILD.gn
+++ b/chrome/browser/autofill/BUILD.gn
@@ -183,11 +183,14 @@
   }
 }
 
-if (!is_android) {
-  source_set("browser_tests") {
-    testonly = true
+source_set("browser_tests") {
+  testonly = true
 
-    sources = [
+  sources = [ "otp_manager_browsertest.cc" ]
+
+  # TODO(crbug.com/449649023) Migrate these tests to PlatformBrowserTests.
+  if (!is_android) {
+    sources += [
       "autocomplete_browsertest.cc",
       "autofill_across_iframes_browsertest.cc",
       "autofill_browsertest.cc",
@@ -195,27 +198,32 @@
       "autofill_server_browsertest.cc",
       "form_structure_browsertest.cc",
     ]
-
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
-
-    deps = [
-      "//base/version_info",
-      "//chrome/browser/autofill",
-      "//chrome/browser/ui:browser_navigator_params_headers",
-      "//chrome/browser/ui/tabs:tab_strip",
-      "//chrome/browser/webdata_services",
-      "//chrome/test:test_support",
-      "//components/autofill/content/browser:test_support",
-      "//components/autofill/core/common:test_support",
-      "//components/password_manager/content/browser:test_support",
-      "//components/ukm:test_support",
-      "//testing/data_driven_testing",
-    ]
-
-    # TODO(crbug.com/40031409): Fix code that adds exit-time destructors and
-    # enable the diagnostic by removing this line.
-    configs += [ "//build/config/compiler:no_exit_time_destructors" ]
   }
+
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
+  deps = [
+    "//base/version_info",
+    "//chrome/browser/autofill",
+    "//chrome/browser/ui:browser_navigator_params_headers",
+    "//chrome/browser/webdata_services",
+    "//chrome/test:test_support",
+    "//components/autofill/content/browser:test_support",
+    "//components/autofill/core/common:test_support",
+    "//components/password_manager/content/browser:test_support",
+    "//components/ukm:test_support",
+    "//testing/data_driven_testing",
+  ]
+
+  if (is_android) {
+    deps += [ "//content/test:browsertest_support" ]
+  } else {
+    deps += [ "//chrome/browser/ui/tabs:tab_strip" ]
+  }
+
+  # TODO(crbug.com/40031409): Fix code that adds exit-time destructors and
+  # enable the diagnostic by removing this line.
+  configs += [ "//build/config/compiler:no_exit_time_destructors" ]
 }
 
 if (!is_android) {
diff --git a/chrome/browser/autofill/DEPS b/chrome/browser/autofill/DEPS
index ba297e7..eef64cc 100644
--- a/chrome/browser/autofill/DEPS
+++ b/chrome/browser/autofill/DEPS
@@ -2,6 +2,7 @@
   '+third_party/libaddressinput/chromium/chrome_metadata_source.h',
   '+third_party/libaddressinput/chromium/chrome_storage_impl.h',
   '+components/browser_ui/device_lock/android',
+  '+components/one_time_tokens/core',
 ]
 
 specific_include_rules = {
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/EditorDialogView.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/EditorDialogView.java
index cbc885a..6baa201 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/EditorDialogView.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/EditorDialogView.java
@@ -34,10 +34,12 @@
 import android.widget.Spinner;
 import android.widget.TextView;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.AlertDialog;
 import androidx.core.view.MarginLayoutParamsCompat;
 
 import org.chromium.base.ResettersForTesting;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.autofill.R;
@@ -119,6 +121,13 @@
 
     private boolean mValidateOnShow;
 
+    @VisibleForTesting
+    public static final String PROFILE_DELETED_HISTOGRAM = "Autofill.ProfileDeleted.Any";
+
+    @VisibleForTesting
+    public static final String PROFILE_DELETED_SETTINGS_HISTOGRAM =
+            "Autofill.ProfileDeleted.Settings";
+
     /**
      * Builds the editor dialog.
      *
@@ -647,6 +656,10 @@
                         .setNegativeButton(
                                 R.string.cancel,
                                 (dialog, which) -> {
+                                    RecordHistogram.recordBooleanHistogram(
+                                            PROFILE_DELETED_HISTOGRAM, false);
+                                    RecordHistogram.recordBooleanHistogram(
+                                            PROFILE_DELETED_SETTINGS_HISTOGRAM, false);
                                     dialog.cancel();
                                     mConfirmationDialog = null;
                                     if (sObserverForTest != null) {
@@ -656,6 +669,10 @@
                         .setPositiveButton(
                                 primaryButtonText,
                                 (dialog, which) -> {
+                                    RecordHistogram.recordBooleanHistogram(
+                                            PROFILE_DELETED_HISTOGRAM, true);
+                                    RecordHistogram.recordBooleanHistogram(
+                                            PROFILE_DELETED_SETTINGS_HISTOGRAM, true);
                                     handleDelete();
                                     mConfirmationDialog = null;
                                 })
diff --git a/chrome/browser/autofill/autocomplete_browsertest.cc b/chrome/browser/autofill/autocomplete_browsertest.cc
index ef69804b..67fb0e1 100644
--- a/chrome/browser/autofill/autocomplete_browsertest.cc
+++ b/chrome/browser/autofill/autocomplete_browsertest.cc
@@ -210,7 +210,9 @@
     form.set_url(GURL("https://www.foo.com"));
     form.set_fields({field});
     autocomplete_history_manager()->OnGetSingleFieldSuggestions(
-        form, field, autofill_manager()->client(), mock_callback.Get());
+        form, /*form_structure=*/nullptr, field,
+        /*trigger_autofill_field=*/nullptr, autofill_manager()->client(),
+        mock_callback.Get());
 
     // Make sure the DB task gets executed.
     WaitForPendingDBTasks(*GetWebDataService());
diff --git a/chrome/browser/autofill/form_structure_browsertest.cc b/chrome/browser/autofill/form_structure_browsertest.cc
index 97dda95a..938aef9 100644
--- a/chrome/browser/autofill/form_structure_browsertest.cc
+++ b/chrome/browser/autofill/form_structure_browsertest.cc
@@ -213,7 +213,6 @@
           // TODO(crbug.com/40266396): Remove once launched.
           features::kAutofillEnableExpirationDateImprovements,
           features::kAutofillIgnoreCheckableElements,
-          features::kAutofillUnifyRationalizationAndSectioningOrder,
           // TODO(crbug.com/369503318): Remove once launched.
           features::kAutofillSupportSplitZipCode,
       },
diff --git a/chrome/browser/autofill/otp_manager_browsertest.cc b/chrome/browser/autofill/otp_manager_browsertest.cc
new file mode 100644
index 0000000..5c5cee6
--- /dev/null
+++ b/chrome/browser/autofill/otp_manager_browsertest.cc
@@ -0,0 +1,285 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/base64.h"
+#include "base/test/run_until.h"
+#include "chrome/test/base/chrome_test_utils.h"
+#include "chrome/test/base/platform_browser_test.h"
+#include "components/autofill/content/browser/content_autofill_driver.h"
+#include "components/autofill/content/browser/test_autofill_client_injector.h"
+#include "components/autofill/content/browser/test_autofill_manager_injector.h"
+#include "components/autofill/content/browser/test_content_autofill_client.h"
+#include "components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_manager.h"
+#include "components/autofill/core/browser/foundations/browser_autofill_manager.h"
+#include "components/autofill/core/browser/foundations/browser_autofill_manager_test_api.h"
+#include "components/autofill/core/browser/foundations/test_autofill_manager_waiter.h"
+#include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
+#include "components/autofill/core/browser/ui/test_autofill_external_delegate.h"
+#include "components/autofill/core/common/signatures.h"
+#include "components/one_time_tokens/core/browser/one_time_token.h"
+#include "components/one_time_tokens/core/browser/sms_otp_backend.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+namespace {
+
+// This implementation is for testing. It lets us manually control and simulate
+// the moment an SMS is received for one-time passwords (OTP).
+class FakeSmsOtpBackend : public one_time_tokens::SmsOtpBackend {
+ public:
+  using CallbackType =
+      base::OnceCallback<void(const one_time_tokens::OtpFetchReply&)>;
+
+  FakeSmsOtpBackend() = default;
+  ~FakeSmsOtpBackend() override = default;
+
+  // one_time_tokens::SmsOtpBackend:
+  void RetrieveSmsOtp(CallbackType callback) override;
+
+  // Simulates the reception of an SMS.
+  void NotifyCallbacks(const one_time_tokens::OtpFetchReply& reply);
+
+  size_t num_callbacks() const { return callbacks_.size(); }
+
+ private:
+  std::vector<CallbackType> callbacks_;
+};
+
+void FakeSmsOtpBackend::RetrieveSmsOtp(
+    FakeSmsOtpBackend::CallbackType callback) {
+  callbacks_.push_back(std::move(callback));
+}
+
+void FakeSmsOtpBackend::NotifyCallbacks(
+    const one_time_tokens::OtpFetchReply& reply) {
+  for (auto& callback : callbacks_) {
+    std::move(callback).Run(reply);
+  }
+  callbacks_.clear();
+}
+
+// AutofillCrowdsourcingManager that classifies every field as a ONE_TIME_CODE.
+class FakeAutofillCrowdsourcingManager : public AutofillCrowdsourcingManager {
+ public:
+  FakeAutofillCrowdsourcingManager(AutofillClient* autofill_client,
+                                   version_info::Channel channel);
+  ~FakeAutofillCrowdsourcingManager() override = default;
+
+  bool StartQueryRequest(
+      const std::vector<raw_ptr<const FormStructure, VectorExperimental>>&
+          forms,
+      std::optional<net::IsolationInfo> isolation_info,
+      base::OnceCallback<void(std::optional<QueryResponse>)> callback) override;
+
+  bool StartUploadRequest(std::vector<AutofillUploadContents> upload_contents,
+                          mojom::SubmissionSource form_submission_source,
+                          bool is_password_manager_upload) override;
+};
+
+FakeAutofillCrowdsourcingManager::FakeAutofillCrowdsourcingManager(
+    AutofillClient* autofill_client,
+    version_info::Channel channel)
+    : AutofillCrowdsourcingManager(autofill_client, channel) {}
+
+bool FakeAutofillCrowdsourcingManager::StartQueryRequest(
+    const std::vector<raw_ptr<const FormStructure, VectorExperimental>>& forms,
+    std::optional<net::IsolationInfo> isolation_info,
+    base::OnceCallback<void(std::optional<QueryResponse>)> callback) {
+  // Generate a response that classifies each field as a ONE_TIME_CODE field.
+  std::vector<FormSignature> queried_form_signatures;
+  AutofillQueryResponse response;
+  for (const FormStructure* form : forms) {
+    queried_form_signatures.push_back(form->form_signature());
+    auto* form_suggestion = response.add_form_suggestions();
+    for (const auto& field : form->fields()) {
+      auto* field_suggestion = form_suggestion->add_field_suggestions();
+      field_suggestion->set_field_signature(
+          CalculateFieldSignatureForField(*field).value());
+      *field_suggestion->add_predictions() =
+          test::CreateFieldPrediction(ONE_TIME_CODE, /*is_override=*/false);
+    }
+  }
+
+  std::move(callback).Run(AutofillCrowdsourcingManager::QueryResponse{
+      base::Base64Encode(response.SerializeAsString()),
+      queried_form_signatures});
+  return true;
+}
+
+bool FakeAutofillCrowdsourcingManager::StartUploadRequest(
+    std::vector<AutofillUploadContents> upload_contents,
+    mojom::SubmissionSource form_submission_source,
+    bool is_password_manager_upload) {
+  // Overridden to prevent network communication from tests.
+  return true;
+}
+
+class OtpTestAutofillManager : public BrowserAutofillManager {
+ public:
+  explicit OtpTestAutofillManager(ContentAutofillDriver* driver)
+      : BrowserAutofillManager(driver) {
+    test_api(*this).SetExternalDelegate(
+        std::make_unique<TestAutofillExternalDelegate>(this));
+  }
+  ~OtpTestAutofillManager() override = default;
+
+  [[nodiscard]] testing::AssertionResult WaitForFormsSeen(
+      int min_num_awaited_calls) {
+    return forms_seen_waiter_.Wait(min_num_awaited_calls);
+  }
+
+  [[nodiscard]] testing::AssertionResult WaitForSuggestionsShown(
+      int min_num_awaited_calls) {
+    return suggestions_shown_waiter_.Wait(min_num_awaited_calls);
+  }
+
+  TestAutofillExternalDelegate& external_delegate() {
+    return *static_cast<TestAutofillExternalDelegate*>(
+        test_api(*this).external_delegate());
+  }
+
+ private:
+  TestAutofillManagerWaiter forms_seen_waiter_{
+      *this,
+      {AutofillManagerEvent::kFormsSeen}};
+
+  TestAutofillManagerWaiter suggestions_shown_waiter_{
+      *this,
+      {AutofillManagerEvent::kAskForValuesToFill}};
+};
+
+class OtpTestAutofillClient : public TestContentAutofillClient {
+ public:
+  explicit OtpTestAutofillClient(content::WebContents* web_contents)
+      : TestContentAutofillClient(web_contents) {
+    set_crowdsourcing_manager(
+        std::make_unique<FakeAutofillCrowdsourcingManager>(
+            this, version_info::Channel::STABLE));
+    set_sms_otp_backend(std::make_unique<FakeSmsOtpBackend>());
+  }
+  ~OtpTestAutofillClient() override = default;
+
+  FakeSmsOtpBackend& sms_otp_backend() {
+    return *static_cast<FakeSmsOtpBackend*>(GetSmsOtpBackend());
+  }
+};
+
+}  // namespace
+
+class OtpManagerBrowserTest : public PlatformBrowserTest {
+  void SetUpOnMainThread() override;
+
+ protected:
+  content::WebContents* web_contents() {
+    return chrome_test_utils::GetActiveWebContents(this);
+  }
+
+  OtpTestAutofillManager& autofill_manager() {
+    return *autofill_manager_injector_[web_contents()];
+  }
+
+  OtpTestAutofillClient& autofill_client() {
+    return *autofill_client_injector_[web_contents()];
+  }
+
+  // The server communication is mocked out, so we don't need to disable it
+  // in the test.
+  test::AutofillBrowserTestEnvironment autofill_test_environment_{
+      {.disable_server_communication = false}};
+
+  TestAutofillClientInjector<OtpTestAutofillClient> autofill_client_injector_;
+  TestAutofillManagerInjector<OtpTestAutofillManager>
+      autofill_manager_injector_;
+};
+
+void OtpManagerBrowserTest::SetUpOnMainThread() {
+  PlatformBrowserTest::SetUpOnMainThread();
+  ASSERT_TRUE(embedded_test_server()->Start());
+}
+
+// Test fixture for the interaction between the WebOTP API and Autofill for
+// OTPs. If the parameter is true, the WebOTP API should be invoked in the test.
+class OtpManagerWithWebOtpApiBrowserTest
+    : public OtpManagerBrowserTest,
+      public testing::WithParamInterface<bool> {
+ protected:
+  bool is_webotp_api_used() const { return GetParam(); }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    OtpManagerWithWebOtpApiBrowserTest,
+    ::testing::Bool());
+
+// This integration test ensures that an OTP is correctly suggested by Autofill
+// by default (when the WebOTP API is not used) but that an OTP does not get
+// autofilled if a website calls the WebOTP API before.
+IN_PROC_BROWSER_TEST_P(OtpManagerWithWebOtpApiBrowserTest,
+                       SmsOtpAutofillIntegrationTest) {
+  GURL url = embedded_test_server()->GetURL("/autofill/sms_otp_form.html");
+
+  // Navigate to page and wait for form to be classified
+  ASSERT_TRUE(chrome_test_utils::NavigateToURL(web_contents(), url));
+  ASSERT_TRUE(autofill_manager().WaitForFormsSeen(1));
+
+  // Immediately when a form field is classified as an OTP field, a subscription
+  // for OTPs should be started if an SMS OTP backend exists.
+  ASSERT_EQ(autofill_client().sms_otp_backend().num_callbacks(), 1u);
+
+  // Using the WebOTP API should block the Autofill OTP feature.
+  if (is_webotp_api_used()) {
+    // Request an OTP via the WebOTP API. The JavaScript code returns true so
+    // ExecJs does not wait for the resolution of the pending Promise.
+    ASSERT_TRUE(ExecJs(web_contents(),
+                       R"(navigator.credentials.get({
+                              otp: {transport:['sms']}
+                          });
+                          true;)",
+                       content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
+                       /*world_id=*/1));
+    // As the WebOTP is asynchronous, wait until the use has been propagated.
+    ASSERT_TRUE(base::test::RunUntil(
+        [&] { return autofill_client().DocumentUsedWebOTP(); }));
+    // From this point on, Autofill should not suggest OTPs anymore.
+  }
+
+  // Simulate an OTP arriving.
+  autofill_client().sms_otp_backend().NotifyCallbacks(
+      one_time_tokens::OtpFetchReply(
+          one_time_tokens::OneTimeToken(
+              one_time_tokens::OneTimeTokenType::kSmsOtp, "123456",
+              base::Time::Now()),
+          /*request_complete=*/true));
+
+  // Simulate click on field.
+  const std::map<FormGlobalId, std::unique_ptr<FormStructure>>& forms =
+      autofill_manager().form_structures();
+  ASSERT_EQ(forms.size(), 1u);
+  const std::unique_ptr<FormStructure>& form = forms.begin()->second;
+  const std::unique_ptr<AutofillField>& first_field = *form->fields().begin();
+  autofill_manager().OnAskForValuesToFill(
+      form->ToFormData(), first_field->global_id(), gfx::Rect(),
+      AutofillSuggestionTriggerSource::kFormControlElementClicked,
+      /*password_request=*/std::nullopt);
+  ASSERT_TRUE(autofill_manager().WaitForSuggestionsShown(1));
+
+  // Verify expectations: The OTP should be suggested by autofill unless the
+  // WebOTP API was called.
+  const TestAutofillExternalDelegate& external_delegate =
+      autofill_manager().external_delegate();
+  const std::vector<Suggestion>& suggestions = external_delegate.suggestions();
+  if (is_webotp_api_used()) {
+    EXPECT_EQ(suggestions.size(), 0u);
+  } else {
+    ASSERT_EQ(suggestions.size(), 1u);
+    EXPECT_EQ(suggestions[0].main_text.value, u"123456");
+  }
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/auxiliary_search/BUILD.gn b/chrome/browser/auxiliary_search/BUILD.gn
index 78f0de2..1b56440 100644
--- a/chrome/browser/auxiliary_search/BUILD.gn
+++ b/chrome/browser/auxiliary_search/BUILD.gn
@@ -11,20 +11,25 @@
 
 source_set("auxiliary_search") {
   sources = [
+    "auxiliary_search_donation_service.h",
+    "auxiliary_search_donation_service_factory.h",
     "auxiliary_search_provider.h",
     "auxiliary_search_top_site_provider_bridge.h",
     "fetch_and_rank_helper.h",
   ]
   public_deps = [
     "//base",
+    "//chrome/browser/profiles:profile",
     "//components/keyed_service/core",
     "//components/ntp_tiles",
+    "//components/page_content_annotations/core",
     "//components/visited_url_ranking/public",
   ]
 }
 
 source_set("impl") {
   sources = [
+    "auxiliary_search_donation_service_factory.cc",
     "auxiliary_search_provider.cc",
     "auxiliary_search_top_site_provider_bridge.cc",
     "fetch_and_rank_helper.cc",
diff --git a/chrome/browser/auxiliary_search/auxiliary_search_donation_service.cc b/chrome/browser/auxiliary_search/auxiliary_search_donation_service.cc
new file mode 100644
index 0000000..e075fbb
--- /dev/null
+++ b/chrome/browser/auxiliary_search/auxiliary_search_donation_service.cc
@@ -0,0 +1,31 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/auxiliary_search/auxiliary_search_donation_service.h"
+
+#include "components/page_content_annotations/core/page_content_annotation_type.h"
+#include "components/page_content_annotations/core/page_content_annotations_common.h"
+#include "components/page_content_annotations/core/page_content_annotations_service.h"
+#include "url/gurl.h"
+
+AuxiliarySearchDonationService::AuxiliarySearchDonationService(
+    page_content_annotations::PageContentAnnotationsService*
+        page_content_annotations_service)
+    : page_content_annotations_service_(page_content_annotations_service) {
+  CHECK(page_content_annotations_service_);
+  page_content_annotations_service_->AddObserver(
+      page_content_annotations::AnnotationType::kContentVisibility, this);
+}
+
+AuxiliarySearchDonationService::~AuxiliarySearchDonationService() {
+  page_content_annotations_service_->RemoveObserver(
+      page_content_annotations::AnnotationType::kContentVisibility, this);
+}
+
+void AuxiliarySearchDonationService::OnPageContentAnnotated(
+    const GURL& url,
+    const page_content_annotations::PageContentAnnotationsResult& result) {
+  // TODO: b/432359106 - Implement this using FetchAndRankHelper and
+  // AuxiliarySearchDonor
+}
diff --git a/chrome/browser/auxiliary_search/auxiliary_search_donation_service.h b/chrome/browser/auxiliary_search/auxiliary_search_donation_service.h
new file mode 100644
index 0000000..02e10d8
--- /dev/null
+++ b/chrome/browser/auxiliary_search/auxiliary_search_donation_service.h
@@ -0,0 +1,42 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_AUXILIARY_SEARCH_AUXILIARY_SEARCH_DONATION_SERVICE_H_
+#define CHROME_BROWSER_AUXILIARY_SEARCH_AUXILIARY_SEARCH_DONATION_SERVICE_H_
+
+#include "base/memory/raw_ptr.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/page_content_annotations/core/page_content_annotations_service.h"
+
+namespace page_content_annotations {
+class PageContentAnnotationsResult;
+}
+class GURL;
+
+// AuxiliarySearchDonationService manages donation of Chrome data to AppSearch.
+// Currently only donates browsing history data.
+class AuxiliarySearchDonationService
+    : public KeyedService,
+      public page_content_annotations::PageContentAnnotationsService::
+          PageContentAnnotationsObserver {
+ public:
+  explicit AuxiliarySearchDonationService(
+      page_content_annotations::PageContentAnnotationsService*
+          page_content_annotations_service);
+  ~AuxiliarySearchDonationService() override;
+
+  // page_content_annotations
+  //     ::PageContentAnnotationsService
+  //     ::PageContentAnnotationsObserver
+  void OnPageContentAnnotated(
+      const GURL& url,
+      const page_content_annotations::PageContentAnnotationsResult& result)
+      override;
+
+ private:
+  raw_ptr<page_content_annotations::PageContentAnnotationsService>
+      page_content_annotations_service_;
+};
+
+#endif  // CHROME_BROWSER_AUXILIARY_SEARCH_AUXILIARY_SEARCH_DONATION_SERVICE_H_
diff --git a/chrome/browser/auxiliary_search/auxiliary_search_donation_service_factory.cc b/chrome/browser/auxiliary_search/auxiliary_search_donation_service_factory.cc
new file mode 100644
index 0000000..5067959
--- /dev/null
+++ b/chrome/browser/auxiliary_search/auxiliary_search_donation_service_factory.cc
@@ -0,0 +1,62 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/auxiliary_search/auxiliary_search_donation_service_factory.h"
+
+#include "base/feature_list.h"
+#include "base/no_destructor.h"
+#include "chrome/browser/auxiliary_search/auxiliary_search_donation_service.h"
+#include "chrome/browser/flags/android/chrome_feature_list.h"
+#include "chrome/browser/page_content_annotations/page_content_annotations_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+// static
+AuxiliarySearchDonationService*
+AuxiliarySearchDonationServiceFactory::GetForProfile(Profile* profile) {
+  return static_cast<AuxiliarySearchDonationService*>(
+      GetInstance()->GetServiceForBrowserContext(/*context=*/profile,
+                                                 /*create=*/true));
+}
+
+// static
+AuxiliarySearchDonationServiceFactory*
+AuxiliarySearchDonationServiceFactory::GetInstance() {
+  static base::NoDestructor<AuxiliarySearchDonationServiceFactory> instance;
+  return instance.get();
+}
+
+AuxiliarySearchDonationServiceFactory::AuxiliarySearchDonationServiceFactory()
+    : ProfileKeyedServiceFactory(
+          "AuxiliarySearchDonationService",
+          ProfileSelections::Builder()
+              .WithRegular(ProfileSelection::kOriginalOnly)
+              .Build()) {
+  DependsOn(PageContentAnnotationsServiceFactory::GetInstance());
+}
+
+AuxiliarySearchDonationServiceFactory::
+    ~AuxiliarySearchDonationServiceFactory() = default;
+
+std::unique_ptr<KeyedService>
+AuxiliarySearchDonationServiceFactory::BuildServiceInstanceForBrowserContext(
+    content::BrowserContext* context) const {
+  if (!base::FeatureList::IsEnabled(
+          chrome::android::kAuxiliarySearchHistoryDonation)) {
+    return nullptr;
+  }
+
+  return std::make_unique<AuxiliarySearchDonationService>(
+      PageContentAnnotationsServiceFactory::GetForProfile(
+          Profile::FromBrowserContext(context)));
+}
+
+bool AuxiliarySearchDonationServiceFactory::ServiceIsCreatedWithBrowserContext()
+    const {
+  return true;
+}
+
+bool AuxiliarySearchDonationServiceFactory::ServiceIsNULLWhileTesting() const {
+  return true;
+}
diff --git a/chrome/browser/auxiliary_search/auxiliary_search_donation_service_factory.h b/chrome/browser/auxiliary_search/auxiliary_search_donation_service_factory.h
new file mode 100644
index 0000000..ff41b5e6
--- /dev/null
+++ b/chrome/browser/auxiliary_search/auxiliary_search_donation_service_factory.h
@@ -0,0 +1,46 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_AUXILIARY_SEARCH_AUXILIARY_SEARCH_DONATION_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_AUXILIARY_SEARCH_AUXILIARY_SEARCH_DONATION_SERVICE_FACTORY_H_
+
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+
+namespace base {
+template <typename T>
+class NoDestructor;
+}  // namespace base
+
+class KeyedService;
+class Profile;
+
+class AuxiliarySearchDonationService;
+
+// Singleton that owns all `AuxiliarySearchDonationService` instances, each
+// mapped to one profile. Listens for profile destructions and clean up the
+// associated AuxiliarySearchDonationServices.
+class AuxiliarySearchDonationServiceFactory
+    : public ProfileKeyedServiceFactory {
+ public:
+  // Returns the `AuxiliarySearchDonationService` instance for `profile`. Create
+  // it if there is no instance.
+  static AuxiliarySearchDonationService* GetForProfile(Profile* profile);
+
+  // Gets the singleton instance of this factory class.
+  static AuxiliarySearchDonationServiceFactory* GetInstance();
+
+ private:
+  friend base::NoDestructor<AuxiliarySearchDonationServiceFactory>;
+
+  AuxiliarySearchDonationServiceFactory();
+  ~AuxiliarySearchDonationServiceFactory() override;
+
+  // ProfileKeyedServiceFactory:
+  std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
+      content::BrowserContext* profile) const override;
+  bool ServiceIsCreatedWithBrowserContext() const override;
+  bool ServiceIsNULLWhileTesting() const override;
+};
+
+#endif  // CHROME_BROWSER_AUXILIARY_SEARCH_AUXILIARY_SEARCH_DONATION_SERVICE_FACTORY_H_
diff --git a/chrome/browser/auxiliary_search/auxiliary_search_provider.cc b/chrome/browser/auxiliary_search/auxiliary_search_provider.cc
index b58f07e..1828ec14 100644
--- a/chrome/browser/auxiliary_search/auxiliary_search_provider.cc
+++ b/chrome/browser/auxiliary_search/auxiliary_search_provider.cc
@@ -44,7 +44,6 @@
 using visited_url_ranking::URLVisitAggregate;
 using visited_url_ranking::URLVisitAggregatesTransformType;
 using visited_url_ranking::URLVisitsMetadata;
-using visited_url_ranking::URLVisitVariantHelper;
 using visited_url_ranking::VisitedURLRankingService;
 using visited_url_ranking::VisitedURLRankingServiceFactory;
 
diff --git a/chrome/browser/auxiliary_search/fetch_and_rank_helper.cc b/chrome/browser/auxiliary_search/fetch_and_rank_helper.cc
index d88a6ac..1e5fcd4 100644
--- a/chrome/browser/auxiliary_search/fetch_and_rank_helper.cc
+++ b/chrome/browser/auxiliary_search/fetch_and_rank_helper.cc
@@ -21,6 +21,7 @@
 #include "components/visited_url_ranking/public/fetch_options.h"
 #include "components/visited_url_ranking/public/url_visit.h"
 #include "components/visited_url_ranking/public/url_visit_util.h"
+#include "third_party/abseil-cpp/absl/functional/overload.h"
 #include "url/android/gurl_android.h"
 #include "url/url_constants.h"
 
@@ -34,7 +35,6 @@
 using visited_url_ranking::URLVisitAggregate;
 using visited_url_ranking::URLVisitAggregatesTransformType;
 using visited_url_ranking::URLVisitsMetadata;
-using visited_url_ranking::URLVisitVariantHelper;
 using visited_url_ranking::VisitedURLRankingService;
 using visited_url_ranking::VisitedURLRankingServiceFactory;
 
@@ -211,7 +211,7 @@
     // take the first one.
     const auto& fetcher_entry = *aggregate.fetcher_data_map.begin();
     std::visit(
-        URLVisitVariantHelper{
+        absl::Overload{
             [&](const URLVisitAggregate::TabData& tab_data) {
               bool is_local_tab =
                   (tab_data.last_active_tab.id != kInvalidTabId);
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarCoordinator.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarCoordinator.java
index a98c564..3283aa0 100644
--- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarCoordinator.java
+++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarCoordinator.java
@@ -297,10 +297,7 @@
 
         mTopControlsStacker = topControlsStacker;
         mTopControlsStacker.addControl(this);
-
-        if (ChromeFeatureList.sBrowserControlsInViz.isEnabled()) {
-            updateOffsetTag();
-        }
+        mTopControlsStacker.requestLayerUpdate(false);
     }
 
     /** Destroys the bookmark bar coordinator. */
@@ -532,21 +529,15 @@
     }
 
     @Override
-    public void onOffsetTagsInfoChanged(
-            BrowserControlsOffsetTagsInfo oldOffsetTagsInfo,
-            BrowserControlsOffsetTagsInfo offsetTagsInfo,
-            int constraints,
-            boolean shouldUpdateOffsets) {
-        if (ChromeFeatureList.sBrowserControlsInViz.isEnabled()) {
-            updateOffsetTag();
-        }
-    }
-
-    @Override
-    public void onControlsPositionChanged(
-            @BrowserControlsStateProvider.ControlsPosition int controlsPosition) {
-        if (ChromeFeatureList.sBrowserControlsInViz.isEnabled()) {
-            updateOffsetTag();
+    public void updateOffsetTag(@Nullable BrowserControlsOffsetTagsInfo offsetTagsInfo) {
+        // The Bookmarks Bar will only be present when the control container is at the top.
+        if (mBrowserControlsStateProvider.getControlsPosition() == ControlsPosition.TOP
+                && offsetTagsInfo != null) {
+            mBookmarkBarSceneLayerModel.set(
+                    BookmarkBarSceneLayerProperties.OFFSET_TAG,
+                    offsetTagsInfo.getTopControlsOffsetTag());
+        } else {
+            mBookmarkBarSceneLayerModel.set(BookmarkBarSceneLayerProperties.OFFSET_TAG, null);
         }
     }
 
@@ -594,17 +585,6 @@
         return mBrowserControlsStateProvider.getTopControlsHeight() - getTopControlHeight();
     }
 
-    private void updateOffsetTag() {
-        // The Bookmarks Bar will only be present when the control container is at the top.
-        if (mBrowserControlsStateProvider.getControlsPosition() == ControlsPosition.TOP) {
-            mBookmarkBarSceneLayerModel.set(
-                    BookmarkBarSceneLayerProperties.OFFSET_TAG,
-                    mTopControlsStacker.getTopControlsOffsetTag());
-        } else {
-            mBookmarkBarSceneLayerModel.set(BookmarkBarSceneLayerProperties.OFFSET_TAG, null);
-        }
-    }
-
     @VisibleForTesting
     void updateBackgroundColor(Tab tab) {
         // We set both the Android widget background and the scene layer background. The scene
@@ -671,8 +651,11 @@
         }
     }
 
-    @VisibleForTesting
     PropertyModel getModelForTesting() {
         return mModel;
     }
+
+    PropertyModel getBookmarkBarSceneLayerModelForTesting() {
+        return mBookmarkBarSceneLayerModel;
+    }
 }
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java
index 9152898a..b4078b7 100644
--- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java
+++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java
@@ -444,7 +444,13 @@
                 BrowserUiListMenuUtils.getBasicListMenu(
                         mActivity,
                         bookmarkItems,
-                        (model) -> model.get(ListMenuItemProperties.CLICK_LISTENER).onClick(null));
+                        (model) -> {
+                            View.OnClickListener clickListener =
+                                    model.get(ListMenuItemProperties.CLICK_LISTENER);
+                            if (clickListener != null) {
+                                clickListener.onClick(null);
+                            }
+                        });
 
         // Go through the entire model list and add the click listeners.
         popupListMenu.setupCallbacksRecursively(
@@ -757,6 +763,11 @@
                     return false;
                 };
 
+        // When building this model, we add both a touch and click listener. This click listener is
+        // to handle AccessibilityServices, which send click events rather than touch events.
+        // Without the listener added here, actions performed on a leaf node in the anchored pop up
+        // will have no effect. Taps, keyboard, and mice all send touch events and do not send click
+        // events, so there are no cases of double events be received.
         PropertyModel model =
                 new PropertyModel.Builder(ListMenuItemProperties.ALL_KEYS)
                         .with(ListMenuItemProperties.TITLE, bookmarkItem.getTitle())
@@ -765,6 +776,15 @@
                         .with(ListMenuItemProperties.IS_TEXT_ELLIPSIZED_AT_END, true)
                         .with(ListMenuItemProperties.ENABLED, true)
                         .with(ListMenuItemProperties.TOUCH_LISTENER, touchListener)
+                        .with(
+                                ListMenuItemProperties.CLICK_LISTENER,
+                                (v) -> {
+                                    // Open url.
+                                    BookmarkBarUtils.recordClick(BookmarkBarClickType.POP_UP_URL);
+                                    mBookmarkOpener.openBookmarkInCurrentTab(
+                                            bookmarkItem.getId(),
+                                            mProfileSupplier.get().isOffTheRecord());
+                                })
                         .build();
         if (mImageFetcher != null) {
             mImageFetcher.fetchFaviconForBookmark(
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarCoordinatorTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarCoordinatorTest.java
index 95bebb2..2892cc2 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarCoordinatorTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarCoordinatorTest.java
@@ -10,7 +10,9 @@
 import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -56,7 +58,9 @@
 import org.chromium.chrome.browser.bookmarks.BookmarkModel;
 import org.chromium.chrome.browser.bookmarks.BookmarkOpener;
 import org.chromium.chrome.browser.bookmarks.FakeBookmarkModel;
+import org.chromium.chrome.browser.browser_controls.BrowserControlsOffsetTagsInfo;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
+import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider.ControlsPosition;
 import org.chromium.chrome.browser.browser_controls.TopControlsStacker;
 import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
@@ -592,4 +596,30 @@
                 expectedDarkTint,
                 bookmarBarModel.get(BookmarkBarProperties.OVERFLOW_BUTTON_TINT_LIST));
     }
+
+    @Test
+    public void testOffsetTags_ControlsAtTop() {
+        doReturn(ControlsPosition.TOP).when(mBrowserControlsManager).getControlsPosition();
+        mCoordinator.updateOffsetTag(new BrowserControlsOffsetTagsInfo());
+        assertNotNull(
+                mCoordinator
+                        .getBookmarkBarSceneLayerModelForTesting()
+                        .get(BookmarkBarSceneLayerProperties.OFFSET_TAG));
+
+        mCoordinator.updateOffsetTag(null);
+        assertNull(
+                mCoordinator
+                        .getBookmarkBarSceneLayerModelForTesting()
+                        .get(BookmarkBarSceneLayerProperties.OFFSET_TAG));
+    }
+
+    @Test
+    public void testOffsetTags_ControlsAtBottom() {
+        doReturn(ControlsPosition.BOTTOM).when(mBrowserControlsManager).getControlsPosition();
+        mCoordinator.updateOffsetTag(new BrowserControlsOffsetTagsInfo());
+        assertNull(
+                mCoordinator
+                        .getBookmarkBarSceneLayerModelForTesting()
+                        .get(BookmarkBarSceneLayerProperties.OFFSET_TAG));
+    }
 }
diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlLayer.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlLayer.java
index 884afe0d..8f1f868 100644
--- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlLayer.java
+++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlLayer.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.browser_controls;
 
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.browser_controls.TopControlsStacker.ScrollBehavior;
 import org.chromium.chrome.browser.browser_controls.TopControlsStacker.TopControlType;
 import org.chromium.chrome.browser.browser_controls.TopControlsStacker.TopControlVisibility;
@@ -49,4 +50,12 @@
      * @param topControlsMinHeight The new minimum height of the top controls.
      */
     default void onTopControlLayerHeightChanged(int topControlsHeight, int topControlsMinHeight) {}
+
+    /**
+     * Interface method to receive OffsetTag updates. Unlike bottom controls, top controls does not
+     * have layers that has additional height that draws beyond its allocated height.
+     *
+     * @param offsetTagsInfo The latest offset tags info. Null if the layer becomes not scrollable.
+     */
+    default void updateOffsetTag(@Nullable BrowserControlsOffsetTagsInfo offsetTagsInfo) {}
 }
diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlsStacker.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlsStacker.java
index cc62bf02..463538d 100644
--- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlsStacker.java
+++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlsStacker.java
@@ -10,7 +10,6 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.cc.input.BrowserControlsState;
-import org.chromium.cc.input.OffsetTag;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.components.browser_ui.util.BrowserControlsVisibilityDelegate;
 
@@ -95,8 +94,7 @@
 
     private int mTotalHeight;
     private int mMinHeight;
-
-    private @Nullable OffsetTag mTopControlsOffsetTag;
+    private @Nullable BrowserControlsOffsetTagsInfo mTopControlsOffsetTagInfo;
 
     /**
      * Constructs the top controls stacker, which is used to calculate heights and offsets for any
@@ -171,16 +169,6 @@
     }
 
     /**
-     * Returns the current OffsetTag for the top controls provided by the {@link
-     * BrowserControlsStateProvider.Observer}.
-     *
-     * @return The OffsetTag for the top controls.
-     */
-    public @Nullable OffsetTag getTopControlsOffsetTag() {
-        return mTopControlsOffsetTag;
-    }
-
-    /**
      * Trigger the browser controls height update based on the current layer status. If there's
      * already an animated transition running, this call might cause it to skip to the end state.
      *
@@ -230,8 +218,17 @@
             if (layer.getTopControlVisibility() != TopControlVisibility.VISIBLE) continue;
 
             totalHeight += layer.getTopControlHeight();
-            if (isLayerAlwaysVisible(layer)) {
+
+            boolean hasMinHeight = isLayerAlwaysVisible(layer);
+            if (hasMinHeight) {
                 minHeight += layer.getTopControlHeight();
+
+                assert minHeight == totalHeight
+                        : "All layers with minHeight should be added before a scrollable layer.";
+            }
+
+            if (ChromeFeatureList.sBrowserControlsInViz.isEnabled()) {
+                layer.updateOffsetTag(hasMinHeight ? null : mTopControlsOffsetTagInfo);
             }
         }
         mTotalHeight = totalHeight;
@@ -288,16 +285,12 @@
             BrowserControlsOffsetTagsInfo offsetTagsInfo,
             @BrowserControlsState int constraints,
             boolean shouldUpdateOffsets) {
-        // TODO(crbug.com/417238089): Consider pushing updated OffsetTags to TopControlLayers.
-        if (mTopControlsOffsetTag == offsetTagsInfo.getTopControlsOffsetTag()
-                && mBrowserControlsState == constraints) {
+        if (mTopControlsOffsetTagInfo == offsetTagsInfo && mBrowserControlsState == constraints) {
             return;
         }
-        mTopControlsOffsetTag = offsetTagsInfo.getTopControlsOffsetTag();
+        mTopControlsOffsetTagInfo = offsetTagsInfo;
         mBrowserControlsState = constraints;
-        if (mScrollingDisabled) {
-            requestLayerUpdate(false);
-        }
+        requestLayerUpdate(false);
     }
 
     /** Tear down |this| and clear all existing controls from the Map. */
diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlsStackerUnitTest.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlsStackerUnitTest.java
index af4db856..be760d8 100644
--- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlsStackerUnitTest.java
+++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/TopControlsStackerUnitTest.java
@@ -20,6 +20,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.cc.input.BrowserControlsState;
 import org.chromium.chrome.browser.browser_controls.TopControlsStacker.ScrollBehavior;
 import org.chromium.chrome.browser.browser_controls.TopControlsStacker.TopControlType;
@@ -37,6 +38,8 @@
         private final boolean mContributesToTotalHeight;
         private final int mHeight;
 
+        private @Nullable BrowserControlsOffsetTagsInfo mOffsetTagsInfo;
+
         TestLayer(
                 @TopControlType int type,
                 @TopControlVisibility int visibility,
@@ -74,6 +77,11 @@
         public int getTopControlHeight() {
             return mHeight;
         }
+
+        @Override
+        public void updateOffsetTag(@Nullable BrowserControlsOffsetTagsInfo offsetTagsInfo) {
+            mOffsetTagsInfo = offsetTagsInfo;
+        }
     }
 
     @Mock private BrowserControlsSizer mBrowserControlsSizer;
@@ -196,55 +204,34 @@
 
     @Test
     public void testMinHeightCalculation() {
+        TestLayer statusIndicator =
+                new TestLayer(
+                        TopControlType.STATUS_INDICATOR,
+                        TopControlVisibility.VISIBLE,
+                        ScrollBehavior.NEVER_SCROLLABLE,
+                        /* contributesToTotalHeight= */ true,
+                        10);
         TestLayer toolbar =
                 new TestLayer(
                         TopControlType.TOOLBAR,
                         TopControlVisibility.VISIBLE,
-                        ScrollBehavior.NEVER_SCROLLABLE,
-                        /* contributesToTotalHeight= */ true,
-                        100);
-        TestLayer tabstrip =
-                new TestLayer(
-                        TopControlType.TABSTRIP,
-                        TopControlVisibility.VISIBLE,
                         ScrollBehavior.DEFAULT_SCROLLABLE,
                         /* contributesToTotalHeight= */ true,
-                        50);
+                        100);
+        mTopControlsStacker.addControl(statusIndicator);
         mTopControlsStacker.addControl(toolbar);
-        mTopControlsStacker.addControl(tabstrip);
         mTopControlsStacker.requestLayerUpdate(false);
 
         Assert.assertEquals(
                 "Total height should be 150.",
-                150,
+                110,
                 mTopControlsStacker.getVisibleTopControlsTotalHeight());
         Assert.assertEquals(
                 "Min height should be 100.",
-                100,
+                10,
                 mTopControlsStacker.getVisibleTopControlsMinHeight());
     }
 
-    @Test
-    public void testScrollingDisabled() {
-        TestLayer toolbar =
-                new TestLayer(
-                        TopControlType.TOOLBAR,
-                        TopControlVisibility.VISIBLE,
-                        ScrollBehavior.DEFAULT_SCROLLABLE,
-                        /* contributesToTotalHeight= */ true,
-                        100);
-        mTopControlsStacker.addControl(toolbar);
-        mTopControlsStacker.onOffsetTagsInfoChanged(
-                new BrowserControlsOffsetTagsInfo(),
-                new BrowserControlsOffsetTagsInfo(),
-                BrowserControlsState.SHOWN,
-                false);
-
-        mTopControlsStacker.setScrollingDisabled(true);
-
-        verify(mBrowserControlsSizer).setTopControlsHeight(100, 100);
-    }
-
     @Test(expected = AssertionError.class)
     public void testAddSameControlTwice() {
         TestLayer toolbar =
@@ -293,7 +280,28 @@
     }
 
     @Test
-    public void testBrowserControlsStateChange() {
+    public void testScrollingDisabled() {
+        TestLayer toolbar =
+                new TestLayer(
+                        TopControlType.TOOLBAR,
+                        TopControlVisibility.VISIBLE,
+                        ScrollBehavior.DEFAULT_SCROLLABLE,
+                        /* contributesToTotalHeight= */ true,
+                        100);
+        mTopControlsStacker.addControl(toolbar);
+        mTopControlsStacker.onOffsetTagsInfoChanged(
+                new BrowserControlsOffsetTagsInfo(),
+                new BrowserControlsOffsetTagsInfo(),
+                BrowserControlsState.SHOWN,
+                false);
+
+        mTopControlsStacker.setScrollingDisabled(true);
+
+        verify(mBrowserControlsSizer).setTopControlsHeight(100, 100);
+    }
+
+    @Test
+    public void testScrollingDisabled_HiddenToShown() {
         TestLayer toolbar =
                 new TestLayer(
                         TopControlType.TOOLBAR,
@@ -303,12 +311,14 @@
                         100);
         mTopControlsStacker.addControl(toolbar);
         mTopControlsStacker.setScrollingDisabled(true);
+        BrowserControlsOffsetTagsInfo offsetTagsInfo = new BrowserControlsOffsetTagsInfo();
         mTopControlsStacker.onOffsetTagsInfoChanged(
                 new BrowserControlsOffsetTagsInfo(),
-                new BrowserControlsOffsetTagsInfo(),
+                offsetTagsInfo,
                 BrowserControlsState.HIDDEN,
                 false);
         verify(mBrowserControlsSizer).setTopControlsHeight(100, 0);
+        assertHasOffsetTags(toolbar, offsetTagsInfo);
 
         // Simulate a browser controls state change without offset tag update.
         reset(mBrowserControlsSizer);
@@ -316,6 +326,92 @@
         verify(mVisibilityDelegate).addObserver(mVisibilityCallbackCaptor.capture());
         mVisibilityCallbackCaptor.getValue().onResult(BrowserControlsState.SHOWN);
         verify(mBrowserControlsSizer).setTopControlsHeight(100, 100);
+        assertHasNoOffsetTags(toolbar);
+    }
+
+    @Test
+    public void testScrollingDisabled_OffsetTagsInfoChanged() {
+        TestLayer toolbar =
+                new TestLayer(
+                        TopControlType.TOOLBAR,
+                        TopControlVisibility.VISIBLE,
+                        ScrollBehavior.DEFAULT_SCROLLABLE,
+                        /* contributesToTotalHeight= */ true,
+                        100);
+        mTopControlsStacker.addControl(toolbar);
+        mTopControlsStacker.setScrollingDisabled(true);
+        verify(mBrowserControlsSizer).setTopControlsHeight(100, 100);
+        reset(mBrowserControlsSizer);
+
+        mTopControlsStacker.onOffsetTagsInfoChanged(
+                new BrowserControlsOffsetTagsInfo(),
+                new BrowserControlsOffsetTagsInfo(),
+                BrowserControlsState.BOTH,
+                false);
+        verify(mBrowserControlsSizer).setTopControlsHeight(100, 100);
+        assertHasNoOffsetTags(toolbar);
+    }
+
+    @Test
+    public void testOffsetTagsInfo_MultipleLayers() {
+        BrowserControlsOffsetTagsInfo offsetTagsInfo = new BrowserControlsOffsetTagsInfo();
+        mTopControlsStacker.onOffsetTagsInfoChanged(
+                new BrowserControlsOffsetTagsInfo(),
+                offsetTagsInfo,
+                BrowserControlsState.BOTH,
+                false);
+
+        TestLayer statusIndicator =
+                new TestLayer(
+                        TopControlType.STATUS_INDICATOR,
+                        TopControlVisibility.VISIBLE,
+                        ScrollBehavior.NEVER_SCROLLABLE,
+                        /* contributesToTotalHeight= */ true,
+                        10);
+        TestLayer toolbar =
+                new TestLayer(
+                        TopControlType.TOOLBAR,
+                        TopControlVisibility.VISIBLE,
+                        ScrollBehavior.DEFAULT_SCROLLABLE,
+                        /* contributesToTotalHeight= */ true,
+                        100);
+
+        mTopControlsStacker.addControl(statusIndicator);
+        mTopControlsStacker.addControl(toolbar);
+        mTopControlsStacker.requestLayerUpdate(false);
+
+        verify(mBrowserControlsSizer).setTopControlsHeight(110, 10);
+        assertHasNoOffsetTags(statusIndicator);
+        assertHasOffsetTags(toolbar, offsetTagsInfo);
+    }
+
+    @Test
+    public void testOffsetTagsInfo_ChangeConstraints() {
+        BrowserControlsOffsetTagsInfo offsetTagsInfo = new BrowserControlsOffsetTagsInfo();
+        mTopControlsStacker.onOffsetTagsInfoChanged(
+                new BrowserControlsOffsetTagsInfo(),
+                offsetTagsInfo,
+                BrowserControlsState.BOTH,
+                false);
+
+        TestLayer toolbar =
+                new TestLayer(
+                        TopControlType.TOOLBAR,
+                        TopControlVisibility.VISIBLE,
+                        ScrollBehavior.DEFAULT_SCROLLABLE,
+                        /* contributesToTotalHeight= */ true,
+                        100);
+        mTopControlsStacker.addControl(toolbar);
+        mTopControlsStacker.requestLayerUpdate(false);
+
+        verify(mBrowserControlsSizer).setTopControlsHeight(100, 0);
+        assertHasOffsetTags(toolbar, offsetTagsInfo);
+
+        BrowserControlsOffsetTagsInfo newOffsetTagsInfo = new BrowserControlsOffsetTagsInfo();
+        mTopControlsStacker.onOffsetTagsInfoChanged(
+                offsetTagsInfo, newOffsetTagsInfo, BrowserControlsState.SHOWN, false);
+        // Assert new offset tags are populated.
+        assertHasOffsetTags(toolbar, newOffsetTagsInfo);
     }
 
     @Test
@@ -359,4 +455,17 @@
                 "Layers not in the current stack can never be at the bottom.",
                 mTopControlsStacker.isLayerAtBottom(TopControlType.HAIRLINE));
     }
+
+    void assertHasOffsetTags(
+            TestLayer layer, @Nullable BrowserControlsOffsetTagsInfo offsetTagsInfo) {
+        Assert.assertEquals(
+                "Scrollable layer should holds offset tags info.",
+                offsetTagsInfo,
+                layer.mOffsetTagsInfo);
+    }
+
+    void assertHasNoOffsetTags(TestLayer layer) {
+        Assert.assertNull(
+                "Unscrollable layer should not have offset tags info", layer.mOffsetTagsInfo);
+    }
 }
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 539b12a..8e56f02 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -180,6 +180,10 @@
 #include "chrome/browser/webapps/webapps_client_android.h"
 #include "chrome/browser/webauthn/android/chrome_webauthn_client_android.h"
 #include "components/webauthn/android/webauthn_client_android.h"
+
+namespace chrome_browser_prefs {
+void OnLocalStatePrefsLoaded();
+}  // namespace chrome_browser_prefs
 #else
 #include "chrome/browser/devtools/devtools_auto_opener.h"
 #include "chrome/browser/error_reporting/chrome_js_error_report_processor.h"
@@ -412,6 +416,10 @@
   MigrateObsoleteLocalStatePrefs(local_state());
   pref_change_registrar_.Init(local_state());
 
+#if BUILDFLAG(IS_ANDROID)
+  chrome_browser_prefs::OnLocalStatePrefsLoaded();
+#endif
+
   // Initialize the notification for the default browser setting policy.
   pref_change_registrar_.Add(
       prefs::kDefaultBrowserSettingEnabled,
diff --git a/chrome/browser/certificate_provider/BUILD.gn b/chrome/browser/certificate_provider/BUILD.gn
new file mode 100644
index 0000000..60214ae
--- /dev/null
+++ b/chrome/browser/certificate_provider/BUILD.gn
@@ -0,0 +1,104 @@
+# Copyright 2025 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_chromeos)
+
+source_set("certificate_provider") {
+  sources = [
+    "certificate_provider.h",
+    "certificate_provider_service.h",
+    "certificate_provider_service_factory.h",
+    "certificate_requests.h",
+    "pin_dialog_manager.h",
+    "security_token_pin_dialog_host.h",
+    "security_token_pin_dialog_host_popup_impl.h",
+    "sign_requests.h",
+    "thread_safe_certificate_map.h",
+  ]
+  public_deps = [
+    "//base",
+    "//chrome/browser/profiles:profile",
+    "//chromeos/components/certificate_provider",
+    "//chromeos/components/security_token_pin",
+    "//components/account_id",
+    "//components/keyed_service/core",
+    "//net",
+  ]
+}
+
+source_set("impl") {
+  sources = [
+    "certificate_provider_service.cc",
+    "certificate_provider_service_factory.cc",
+    "certificate_requests.cc",
+    "pin_dialog_manager.cc",
+    "security_token_pin_dialog_host_popup_impl.cc",
+    "sign_requests.cc",
+    "thread_safe_certificate_map.cc",
+  ]
+  deps = [
+    ":certificate_provider",
+    "//chrome/browser/ui/ash/login",
+    "//chrome/common/extensions",
+    "//chrome/common/extensions/api",
+    "//extensions/browser",
+    "//extensions/common",
+    "//third_party/boringssl",
+    "//ui/aura",
+    "//ui/gfx",
+    "//ui/views",
+
+    # Remove this dependency when the following headers get componentized:
+    # - c/b/ui/browser.h, browser_finder.h, browser_window.h
+    # - c/b/ui/views/notifications/request_pin_view_chromeos.h
+    "//chrome/browser/ui",
+  ]
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+}
+
+source_set("test_support") {
+  testonly = true
+  sources = [
+    "test_certificate_provider_extension.cc",
+    "test_certificate_provider_extension.h",
+    "test_certificate_provider_extension_mixin.cc",
+    "test_certificate_provider_extension_mixin.h",
+  ]
+  public_deps = [
+    "//base",
+    "//chrome/test:test_support_ui",
+    "//crypto",
+    "//extensions:test_support",
+    "//extensions/common",
+    "//net",
+  ]
+  deps = [
+    "//chrome/browser/profiles:profile",
+    "//chrome/common:constants",
+    "//chrome/common/extensions",
+    "//chrome/common/extensions/api",
+    "//chrome/test:test_support",
+    "//content/public/browser",
+    "//extensions/browser",
+    "//extensions/browser/api/test",
+    "//extensions/common/api",
+    "//net:test_support",
+    "//testing/gtest",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [ "certificate_provider_service_unittest.cc" ]
+  deps = [
+    ":certificate_provider",
+    "//base",
+    "//base/test:test_support",
+    "//net",
+    "//net:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/boringssl",
+  ]
+}
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 03aef97..3630f92 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -799,10 +799,6 @@
   if (!device_event_log::IsInitialized())
     device_event_log::Initialize(0 /* default max entries */);
 
-  // Set up and register ERP reporting client.
-  reporting_client_ =
-      reporting::ReportingClient::Create(content::GetUIThreadTaskRunner({}));
-
   for (auto& chrome_extra_part : chrome_extra_parts_)
     chrome_extra_part->PostCreateMainMessageLoop();
 }
diff --git a/chrome/browser/chrome_browser_main.h b/chrome/browser/chrome_browser_main.h
index 9d0006ae..af457d7f 100644
--- a/chrome/browser/chrome_browser_main.h
+++ b/chrome/browser/chrome_browser_main.h
@@ -15,7 +15,6 @@
 #include "build/build_config.h"
 #include "chrome/browser/buildflags.h"
 #include "chrome/browser/first_run/first_run.h"
-#include "chrome/browser/policy/messaging_layer/public/report_client.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
 #include "chrome/common/buildflags.h"
 #include "content/public/browser/browser_main_parts.h"
@@ -178,10 +177,6 @@
 
   std::unique_ptr<content::SyntheticTrialSyncer> synthetic_trial_syncer_;
 
-  // ERP client instance, serving all reporting needs in the browser.
-  reporting::ReportQueueProvider::SmartPtr<reporting::ReportingClient>
-      reporting_client_{nullptr, base::OnTaskRunnerDeleter(nullptr)};
-
   // Members initialized after / released before main_message_loop_ ------------
 
   std::unique_ptr<BrowserProcessImpl> browser_process_;
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 9ded8c9..8896c94 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -52,7 +52,6 @@
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
 #include "build/config/chromebox_for_meetings/buildflags.h"  // PLATFORM_CFM
-#include "chrome/browser/preloading/search_preload/search_preload_features.h"
 #include "chrome/browser/after_startup_task_utils.h"
 #include "chrome/browser/ai/ai_manager.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
@@ -81,6 +80,7 @@
 #include "chrome/browser/download/chrome_download_manager_delegate.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/enterprise/browser_management/management_service_factory.h"
+#include "chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.h"
 #include "chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_service.h"
 #include "chrome/browser/enterprise/reporting/prefs.h"
 #include "chrome/browser/enterprise/util/managed_browser_utils.h"
@@ -133,6 +133,7 @@
 #include "chrome/browser/preloading/preloading_features.h"
 #include "chrome/browser/preloading/preloading_prefs.h"
 #include "chrome/browser/preloading/prerender/prerender_web_contents_delegate.h"
+#include "chrome/browser/preloading/search_preload/search_preload_features.h"
 #include "chrome/browser/privacy_budget/identifiability_study_state.h"
 #include "chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h"
 #include "chrome/browser/privacy_sandbox/tracking_protection_settings_factory.h"
@@ -588,10 +589,6 @@
 #include "components/crash/content/browser/crash_handler_host_linux.h"
 #endif
 
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-#include "chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.h"
-#endif
-
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
     BUILDFLAG(IS_CHROMEOS)
 #include "components/webapps/isolated_web_apps/scheme.h"
@@ -1717,11 +1714,9 @@
 
   chrome::AddMetricsExtraParts(main_parts.get());
 
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
   main_parts->AddParts(
       std::make_unique<
           enterprise_util::ChromeBrowserMainExtraPartsEnterprise>());
-#endif
 
 #if !BUILDFLAG(IS_ANDROID)
   main_parts->AddParts(
diff --git a/chrome/browser/chromeos/extensions/login_screen/login_state/BUILD.gn b/chrome/browser/chromeos/extensions/login_screen/login_state/BUILD.gn
index b6a6056..0e5a90c 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login_state/BUILD.gn
+++ b/chrome/browser/chromeos/extensions/login_screen/login_state/BUILD.gn
@@ -25,6 +25,7 @@
   deps = [
     "//chrome/browser/profiles:profile",
     "//chrome/common:non_code_constants",
+    "//chromeos/ash/components/browser_context_helper",
     "//components/session_manager:base",
     "//components/session_manager/core",
     "//content/public/browser",
@@ -66,6 +67,7 @@
     "//chrome/common:non_code_constants",
     "//chrome/common/extensions/api",
     "//chrome/test:test_support",
+    "//chromeos/ash/components/browser_context_helper",
     "//chromeos/ash/components/login/login_state",
     "//components/session_manager:base",
     "//components/session_manager/core",
diff --git a/chrome/browser/chromeos/extensions/login_screen/login_state/login_state_api.cc b/chrome/browser/chromeos/extensions/login_screen/login_state/login_state_api.cc
index 2633765c..a69ba57 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login_state/login_state_api.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login_state/login_state_api.cc
@@ -7,16 +7,11 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/extensions/api/login_state.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_types.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/session_manager_types.h"
 #include "content/public/browser/browser_context.h"
 
-namespace {
-bool IsSigninProfile(const Profile* profile) {
-  return profile && profile->GetBaseName().value() == chrome::kInitialProfile;
-}
-}  // namespace
-
 namespace extensions {
 
 api::login_state::SessionState ToApiEnum(session_manager::SessionState state) {
@@ -41,11 +36,16 @@
 }
 
 ExtensionFunction::ResponseAction LoginStateGetProfileTypeFunction::Run() {
-  bool is_signin_profile =
-      IsSigninProfile(Profile::FromBrowserContext(browser_context()));
-  api::login_state::ProfileType profile_type =
-      is_signin_profile ? api::login_state::ProfileType::kSigninProfile
-                        : api::login_state::ProfileType::kUserProfile;
+  const Profile* profile = Profile::FromBrowserContext(browser_context());
+  api::login_state::ProfileType profile_type;
+
+  if (ash::IsSigninBrowserContext(profile)) {
+    profile_type = api::login_state::ProfileType::kSigninProfile;
+  } else if (ash::IsLockScreenBrowserContext(profile)) {
+    profile_type = api::login_state::ProfileType::kLockProfile;
+  } else {
+    profile_type = api::login_state::ProfileType::kUserProfile;
+  }
   return RespondNow(WithArguments(api::login_state::ToString(profile_type)));
 }
 
diff --git a/chrome/browser/chromeos/extensions/login_screen/login_state/login_state_api_unittest.cc b/chrome/browser/chromeos/extensions/login_screen/login_state/login_state_api_unittest.cc
index 2eb24c22..3aaa6e3a 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login_state/login_state_api_unittest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login_state/login_state_api_unittest.cc
@@ -6,11 +6,13 @@
 
 #include <memory>
 
+#include "base/files/file_path.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/extension_api_unittest.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_types.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/session_manager_types.h"
 #include "extensions/browser/api_test_utils.h"
@@ -69,6 +71,23 @@
                                   ->GetString());
 }
 
+// Test that |loginState.getProfileType()| returns |LOCK_PROFILE| for
+// extensions running in the lock profile.
+TEST_F(LoginStateApiUnittest, GetProfileType_LockProfile) {
+  // |ash::ProfileHelper::GetLockScreenProfile()| cannot be used as the
+  // |TestingProfileManager| set up by |BrowserWithTestWindowTest| has an empty
+  // user data directory.
+  TestingProfile::Builder builder;
+  builder.SetPath(base::FilePath(
+      FILE_PATH_LITERAL(ash::kLockScreenBrowserContextBaseName)));
+  std::unique_ptr<Profile> profile = builder.Build();
+
+  auto function = base::MakeRefCounted<LoginStateGetProfileTypeFunction>();
+  EXPECT_EQ("LOCK_PROFILE", api_test_utils::RunFunctionAndReturnSingleResult(
+                                function.get(), "[]", profile.get())
+                                ->GetString());
+}
+
 class LoginStateApiAshUnittest : public LoginStateApiUnittest {
  public:
   LoginStateApiAshUnittest() = default;
diff --git a/chrome/browser/chromeos/extensions/login_screen/login_state/login_state_apitest.cc b/chrome/browser/chromeos/extensions/login_screen/login_state/login_state_apitest.cc
index 3e92314..9fbdddf 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login_state/login_state_apitest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login_state/login_state_apitest.cc
@@ -12,6 +12,7 @@
 
 using LoginStateApitest = ExtensionApiTest;
 
+// TODO(crbug.com/448041262): Add a test case for LOCK_PROFILE.
 // Test that |loginState.getProfileType()| returns |USER_PROFILE| for
 // extensions not running in the signin profile.
 IN_PROC_BROWSER_TEST_F(LoginStateApitest, GetProfileType_UserProfile) {
diff --git a/chrome/browser/chromeos/video_conference/video_conference_manager_client_browsertest.cc b/chrome/browser/chromeos/video_conference/video_conference_manager_client_browsertest.cc
index c160f3bb..a53ddd4 100644
--- a/chrome/browser/chromeos/video_conference/video_conference_manager_client_browsertest.cc
+++ b/chrome/browser/chromeos/video_conference/video_conference_manager_client_browsertest.cc
@@ -68,12 +68,12 @@
     return VideoConferenceManagerClientImpl::GetAggregatedPermissions();
   }
 
-  bool camera_system_disabled() {
-    return media_listener_->camera_system_disabled_;
+  bool camera_system_enabled() {
+    return media_listener_->camera_system_enabled_;
   }
 
-  bool microphone_system_disabled() {
-    return media_listener_->microphone_system_disabled_;
+  bool microphone_system_enabled() {
+    return media_listener_->microphone_system_enabled_;
   }
 
   crosapi::mojom::VideoConferenceMediaUsageStatusPtr& status() {
@@ -248,32 +248,32 @@
   ASSERT_TRUE(controller);
   EXPECT_EQ(controller->device_used_while_disabled_records().size(), 0u);
 
-  EXPECT_FALSE(client.camera_system_disabled());
-  EXPECT_FALSE(client.microphone_system_disabled());
+  EXPECT_TRUE(client.camera_system_enabled());
+  EXPECT_TRUE(client.microphone_system_enabled());
 
   vc_manager->SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice::kCamera,
-      /*disabled=*/true);
-  EXPECT_TRUE(client.camera_system_disabled());
-  EXPECT_FALSE(client.microphone_system_disabled());
+      /*enabled=*/false);
+  EXPECT_FALSE(client.camera_system_enabled());
+  EXPECT_TRUE(client.microphone_system_enabled());
 
   vc_manager->SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice::kMicrophone,
-      /*disabled=*/true);
-  EXPECT_TRUE(client.camera_system_disabled());
-  EXPECT_TRUE(client.microphone_system_disabled());
+      /*enabled=*/false);
+  EXPECT_FALSE(client.camera_system_enabled());
+  EXPECT_FALSE(client.microphone_system_enabled());
 
   vc_manager->SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice::kMicrophone,
-      /*disabled=*/false);
-  EXPECT_TRUE(client.camera_system_disabled());
-  EXPECT_FALSE(client.microphone_system_disabled());
+      /*enabled=*/true);
+  EXPECT_FALSE(client.camera_system_enabled());
+  EXPECT_TRUE(client.microphone_system_enabled());
 
   vc_manager->SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice::kCamera,
-      /*disabled=*/false);
-  EXPECT_FALSE(client.camera_system_disabled());
-  EXPECT_FALSE(client.microphone_system_disabled());
+      /*enabled=*/true);
+  EXPECT_TRUE(client.camera_system_enabled());
+  EXPECT_TRUE(client.microphone_system_enabled());
 }
 
 // Tests client updates relating to adding and removing VC web apps and title
diff --git a/chrome/browser/chromeos/video_conference/video_conference_media_listener.cc b/chrome/browser/chromeos/video_conference/video_conference_media_listener.cc
index 1308aec4..6521d7d 100644
--- a/chrome/browser/chromeos/video_conference/video_conference_media_listener.cc
+++ b/chrome/browser/chromeos/video_conference/video_conference_media_listener.cc
@@ -37,13 +37,13 @@
 
 void VideoConferenceMediaListener::SetSystemMediaDeviceStatus(
     crosapi::mojom::VideoConferenceMediaDevice device,
-    bool disabled) {
+    bool enabled) {
   switch (device) {
     case crosapi::mojom::VideoConferenceMediaDevice::kCamera:
-      camera_system_disabled_ = disabled;
+      camera_system_enabled_ = enabled;
       break;
     case crosapi::mojom::VideoConferenceMediaDevice::kMicrophone:
-      microphone_system_disabled_ = disabled;
+      microphone_system_enabled_ = enabled;
       break;
     case crosapi::mojom::VideoConferenceMediaDevice::kUnusedDefault:
       return;
@@ -75,7 +75,7 @@
 
     // This will be an AnchoredNudge, which is only visible if the tray is
     // visible; so we have to call this after media_usage_update_callback_.
-    if (camera_system_disabled_ && !prev_is_capturing_video &&
+    if (!camera_system_enabled_ && !prev_is_capturing_video &&
         is_capturing_video) {
       device_used_while_disabled_callback_.Run(
           crosapi::mojom::VideoConferenceMediaDevice::kCamera,
@@ -110,7 +110,7 @@
 
     // This will be an AnchoredNudge, which is only visible if the tray is
     // visible; so we have to call this after media_usage_update_callback_.
-    if (microphone_system_disabled_ && !prev_is_capturing_audio &&
+    if (!microphone_system_enabled_ && !prev_is_capturing_audio &&
         is_capturing_audio) {
       device_used_while_disabled_callback_.Run(
           crosapi::mojom::VideoConferenceMediaDevice::kMicrophone,
diff --git a/chrome/browser/chromeos/video_conference/video_conference_media_listener.h b/chrome/browser/chromeos/video_conference/video_conference_media_listener.h
index d91cf66..7c907cd7 100644
--- a/chrome/browser/chromeos/video_conference/video_conference_media_listener.h
+++ b/chrome/browser/chromeos/video_conference/video_conference_media_listener.h
@@ -40,7 +40,7 @@
 
   void SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice device,
-      bool disabled);
+      bool enabled);
 
   // MediaStreamCaptureIndicator::Observer overrides
   void OnIsCapturingVideoChanged(content::WebContents* contents,
@@ -69,10 +69,10 @@
   void OnIsCapturingScreenChanged(content::WebContents* contents,
                                   bool is_capturing_screen);
 
-  // The following two fields are true if the camera/microphone is system-wide
+  // The following two fields are false if the camera/microphone is system-wide
   // software disabled OR disabled via a hardware switch.
-  bool camera_system_disabled_{false};
-  bool microphone_system_disabled_{false};
+  bool camera_system_enabled_{true};
+  bool microphone_system_enabled_{true};
 
   base::RepeatingCallback<void()> media_usage_update_callback_;
   base::RepeatingCallback<VideoConferenceWebApp*(content::WebContents*)>
diff --git a/chrome/browser/chromeos/video_conference/video_conference_media_listener_browsertest.cc b/chrome/browser/chromeos/video_conference/video_conference_media_listener_browsertest.cc
index d3a5ef3..715018efc 100644
--- a/chrome/browser/chromeos/video_conference/video_conference_media_listener_browsertest.cc
+++ b/chrome/browser/chromeos/video_conference/video_conference_media_listener_browsertest.cc
@@ -313,7 +313,7 @@
   auto* vc_app2 = CreateVcWebAppInNewTab();
 
   vc_manager->SetSystemMediaDeviceStatus(
-      crosapi::mojom::VideoConferenceMediaDevice::kCamera, /*disabled=*/true);
+      crosapi::mojom::VideoConferenceMediaDevice::kCamera, /*enabled=*/false);
 
   // Initially should be zero.
   EXPECT_EQ(controller->device_used_while_disabled_records().size(), 0u);
@@ -327,7 +327,7 @@
 
   vc_manager->SetSystemMediaDeviceStatus(
       crosapi::mojom::VideoConferenceMediaDevice::kMicrophone,
-      /*disabled=*/true);
+      /*enabled=*/false);
   auto stop_capture_callback2 =
       StartCapture(&vc_app2->GetWebContents(),
                    blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE);
diff --git a/chrome/browser/content_settings/host_content_settings_map_unittest.cc b/chrome/browser/content_settings/host_content_settings_map_unittest.cc
index d8665542..23a3de8 100644
--- a/chrome/browser/content_settings/host_content_settings_map_unittest.cc
+++ b/chrome/browser/content_settings/host_content_settings_map_unittest.cc
@@ -1366,8 +1366,8 @@
 
 TEST_F(HostContentSettingsMapTest, IncognitoDontInheritContentSetting) {
   // Content settings marked DONT_INHERIT_IN_INCOGNITO in
-  // ContentSettingsRegistry (e.g. top-level scoped 3pcd, which is a special
-  // case) don't inherit any values from from regular to incognito.
+  // ContentSettingsRegistry (e.g. Storage Access Header origin trial, which is
+  // a special case) don't inherit any values from from regular to incognito.
   TestingProfile profile;
   Profile* otr_profile =
       profile.GetPrimaryOTRProfile(/*create_if_needed=*/true);
@@ -1378,25 +1378,29 @@
 
   GURL host("https://example.com/");
 
-  // top-level scoped 3pcd defaults to ALLOW.
-  EXPECT_EQ(CONTENT_SETTING_ALLOW,
-            map->GetContentSetting(
-                host, host, ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL));
-  EXPECT_EQ(CONTENT_SETTING_ALLOW,
-            otr_map->GetContentSetting(
-                host, host, ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL));
+  // Storage Access Header origin trial content settings defaults to BLOCK.
+  EXPECT_EQ(
+      CONTENT_SETTING_BLOCK,
+      map->GetContentSetting(
+          host, host, ContentSettingsType::STORAGE_ACCESS_HEADER_ORIGIN_TRIAL));
+  EXPECT_EQ(
+      CONTENT_SETTING_BLOCK,
+      otr_map->GetContentSetting(
+          host, host, ContentSettingsType::STORAGE_ACCESS_HEADER_ORIGIN_TRIAL));
 
   map->SetContentSettingDefaultScope(
-      host, GURL(), ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL,
-      CONTENT_SETTING_BLOCK);
+      host, host, ContentSettingsType::STORAGE_ACCESS_HEADER_ORIGIN_TRIAL,
+      CONTENT_SETTING_ALLOW);
 
   // The setting is not inherited by |otr_map|.
-  EXPECT_EQ(CONTENT_SETTING_BLOCK,
-            map->GetContentSetting(
-                host, host, ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL));
-  EXPECT_EQ(CONTENT_SETTING_ALLOW,
-            otr_map->GetContentSetting(
-                host, host, ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL));
+  EXPECT_EQ(
+      CONTENT_SETTING_ALLOW,
+      map->GetContentSetting(
+          host, host, ContentSettingsType::STORAGE_ACCESS_HEADER_ORIGIN_TRIAL));
+  EXPECT_EQ(
+      CONTENT_SETTING_BLOCK,
+      otr_map->GetContentSetting(
+          host, host, ContentSettingsType::STORAGE_ACCESS_HEADER_ORIGIN_TRIAL));
 }
 
 TEST_F(HostContentSettingsMapTest, PrefExceptionsOperation) {
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_helper_unittest.cc b/chrome/browser/contextual_cueing/contextual_cueing_helper_unittest.cc
index f228fa20..59cd6a38 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_helper_unittest.cc
+++ b/chrome/browser/contextual_cueing/contextual_cueing_helper_unittest.cc
@@ -42,7 +42,8 @@
 std::unique_ptr<KeyedService> CreatePageContentExtractionService(
     content::BrowserContext* context) {
   return std::make_unique<
-      page_content_annotations::PageContentExtractionService>();
+      page_content_annotations::PageContentExtractionService>(
+      /*os_crypt_async=*/nullptr, context->GetPath());
 }
 
 std::unique_ptr<KeyedService> CreateContextualCueingService(
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_service_unittest.cc b/chrome/browser/contextual_cueing/contextual_cueing_service_unittest.cc
index 60a21d4..c5bca794 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_service_unittest.cc
+++ b/chrome/browser/contextual_cueing/contextual_cueing_service_unittest.cc
@@ -44,6 +44,8 @@
 
 class ContextualCueingServiceTest : public testing::Test {
  public:
+  ContextualCueingServiceTest()
+      : page_content_extraction_service_(nullptr, base::FilePath()) {}
   virtual void InitializeFeatureList() {
     scoped_feature_list_.InitWithFeaturesAndParameters(
         {{contextual_cueing::kContextualCueing,
diff --git a/chrome/browser/controlled_frame/controlled_frame_permissions_unittest.cc b/chrome/browser/controlled_frame/controlled_frame_permissions_unittest.cc
index f7408ec..e9bdec0 100644
--- a/chrome/browser/controlled_frame/controlled_frame_permissions_unittest.cc
+++ b/chrome/browser/controlled_frame/controlled_frame_permissions_unittest.cc
@@ -180,7 +180,6 @@
       case ContentSettingsType::TPCD_METADATA_GRANTS:
       case ContentSettingsType::TPCD_TRIAL:
       case ContentSettingsType::TOP_LEVEL_TPCD_TRIAL:
-      case ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL:
       case ContentSettingsType::AUTO_PICTURE_IN_PICTURE:
       case ContentSettingsType::FILE_SYSTEM_ACCESS_EXTENDED_PERMISSION:
       case ContentSettingsType::FILE_SYSTEM_ACCESS_RESTORE_PERMISSION:
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index 7909c3f..ba3281c 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -1824,6 +1824,16 @@
                       std::move(ai_code_completion_dict));
   }
 
+  if (base::FeatureList::IsEnabled(
+          ::features::kDevToolsEnableDurableMessages)) {
+    base::Value::Dict devtools_durable_message_dict;
+    devtools_durable_message_dict.Set(
+        "enabled",
+        base::FeatureList::IsEnabled(features::kDevToolsEnableDurableMessages));
+    response_dict.Set("devToolsEnableDurableMessages",
+                      std::move(devtools_durable_message_dict));
+  }
+
   base::Value::Dict devtools_well_known_dict;
   devtools_well_known_dict.Set(
       "enabled", base::FeatureList::IsEnabled(::features::kDevToolsWellKnown));
diff --git a/chrome/browser/devtools/features.cc b/chrome/browser/devtools/features.cc
index ea8ba5e0..c1f9dca9 100644
--- a/chrome/browser/devtools/features.cc
+++ b/chrome/browser/devtools/features.cc
@@ -185,4 +185,7 @@
 BASE_FEATURE(kDevToolsStartingStyleDebugging,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Whether Network panel should use Durable Messages to preserve network bodies.
+BASE_FEATURE(kDevToolsEnableDurableMessages, base::FEATURE_ENABLED_BY_DEFAULT);
+
 }  // namespace features
diff --git a/chrome/browser/devtools/features.h b/chrome/browser/devtools/features.h
index 791e095..81cd54a 100644
--- a/chrome/browser/devtools/features.h
+++ b/chrome/browser/devtools/features.h
@@ -112,6 +112,8 @@
 
 BASE_DECLARE_FEATURE(kDevToolsStartingStyleDebugging);
 
+BASE_DECLARE_FEATURE(kDevToolsEnableDurableMessages);
+
 }  // namespace features
 
 #endif  // CHROME_BROWSER_DEVTOOLS_FEATURES_H_
diff --git a/chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.cc b/chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.cc
index 4cbae5dc..e744ad9 100644
--- a/chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.cc
+++ b/chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.cc
@@ -5,11 +5,13 @@
 #include "chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.h"
 
 #include "components/enterprise/buildflags/buildflags.h"
+#include "content/public/browser/browser_thread.h"
 
-#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
-#include "chrome/browser/enterprise/connectors/analysis/content_analysis_sdk_manager.h"
-#include "chrome/browser/enterprise/connectors/connectors_service.h"
-#endif  // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
+#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)) && \
+    BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
+#include "chrome/browser/enterprise/connectors/analysis/content_analysis_sdk_manager.h"  // nogncheck
+#include "chrome/browser/enterprise/connectors/connectors_service.h"  // nogncheck
+#endif
 
 namespace enterprise_util {
 
@@ -51,12 +53,19 @@
 ChromeBrowserMainExtraPartsEnterprise::
     ~ChromeBrowserMainExtraPartsEnterprise() = default;
 
+#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)) && \
+    BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
 void ChromeBrowserMainExtraPartsEnterprise::PostProfileInit(
     Profile* profile,
     bool is_initial_profile) {
-#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
   MaybePrimeLocalContentAnalysisAgentConnection(profile);
-#endif  // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
+}
+#endif
+
+void ChromeBrowserMainExtraPartsEnterprise::PostCreateMainMessageLoop() {
+  // Set up and register ERP reporting client.
+  reporting_client_ =
+      reporting::ReportingClient::Create(content::GetUIThreadTaskRunner({}));
 }
 
 }  // namespace enterprise_util
diff --git a/chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.h b/chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.h
index 2d3ed6c..a7b4ad50 100644
--- a/chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.h
+++ b/chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.h
@@ -5,7 +5,10 @@
 #ifndef CHROME_BROWSER_ENTERPRISE_CHROME_BROWSER_MAIN_EXTRA_PARTS_ENTERPRISE_H_
 #define CHROME_BROWSER_ENTERPRISE_CHROME_BROWSER_MAIN_EXTRA_PARTS_ENTERPRISE_H_
 
+#include "base/task/sequenced_task_runner.h"
 #include "chrome/browser/chrome_browser_main_extra_parts.h"
+#include "chrome/browser/policy/messaging_layer/public/report_client.h"
+#include "components/enterprise/buildflags/buildflags.h"
 
 namespace enterprise_util {
 
@@ -22,7 +25,16 @@
   ~ChromeBrowserMainExtraPartsEnterprise() override;
 
   // ChromeBrowserMainExtraParts:
+#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)) && \
+    BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
   void PostProfileInit(Profile* profile, bool is_initial_profile) override;
+#endif
+  void PostCreateMainMessageLoop() override;
+
+ private:
+  // ERP client instance, serving all reporting needs in the browser.
+  reporting::ReportQueueProvider::SmartPtr<reporting::ReportingClient>
+      reporting_client_{nullptr, base::OnTaskRunnerDeleter(nullptr)};
 };
 
 }  // namespace enterprise_util
diff --git a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.cc b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.cc
index bee79b9..1a8b77e1 100644
--- a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.cc
+++ b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.cc
@@ -18,15 +18,14 @@
 
 AnalysisServiceSettings::AnalysisServiceSettings(
     const base::Value& settings_value,
-    const ServiceProviderConfig& service_provider_config) {
-  if (!settings_value.is_dict()) {
+    const ServiceProviderConfig& service_provider_config)
+    : AnalysisServiceSettingsBase(settings_value, service_provider_config) {
+  if (!analysis_config_) {
+    // Parsing in the base class failed
     return;
   }
 
   const auto& settings_dict = settings_value.GetDict();
-  if (!TryParseServiceProviderData(settings_dict, service_provider_config)) {
-    return;
-  }
 
   // Add the patterns to the settings, which configures settings.matcher and
   // settings.*_pattern_settings. No enable patterns implies the settings are
@@ -41,10 +40,6 @@
   ParsePatternSettings(enabled_pattern_settings_list, true, id);
   ParsePatternSettings(settings_dict.FindList(kKeyDisable), false, id);
 
-  ParseBlockSettings(settings_dict);
-  ParseMinimumDataSize(settings_dict);
-  ParseCustomMessages(settings_dict);
-  ParseJustificationTags(settings_dict);
 #if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
   ParseVerificationSignatures(settings_dict);
 #endif
@@ -75,29 +70,6 @@
 }
 #endif
 
-// static
-std::optional<AnalysisServiceSettings::URLPatternSettings>
-AnalysisServiceSettings::GetPatternSettings(
-    const PatternSettings& patterns,
-    base::MatcherStringPattern::ID match) {
-  // If the pattern exists directly in the map, return its settings.
-  if (patterns.count(match) == 1) {
-    return patterns.at(match);
-  }
-
-  // If the pattern doesn't exist in the map, it might mean that it wasn't the
-  // only pattern to correspond to its settings and that the ID added to
-  // the map was the one of the last pattern corresponding to those settings.
-  // This means the next match ID greater than |match| has the correct
-  // settings if it exists.
-  auto next = patterns.upper_bound(match);
-  if (next != patterns.end()) {
-    return next->second;
-  }
-
-  return std::nullopt;
-}
-
 AnalysisSettings AnalysisServiceSettings::GetAnalysisSettingsWithTags(
     std::map<std::string, TagSettings> tags,
     DataRegion data_region) const {
@@ -186,59 +158,6 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
-bool AnalysisServiceSettings::ShouldBlockUntilVerdict() const {
-  if (!IsValid()) {
-    return false;
-  }
-
-  return block_until_verdict_ == BlockUntilVerdict::kBlock;
-}
-
-bool AnalysisServiceSettings::ShouldBlockByDefault() const {
-  if (!IsValid()) {
-    return false;
-  }
-
-  return default_action_ == DefaultAction::kBlock;
-}
-
-std::optional<std::u16string> AnalysisServiceSettings::GetCustomMessage(
-    const std::string& tag) {
-  const auto& element = tags_.find(tag);
-
-  if (!IsValid() || element == tags_.end() ||
-      element->second.custom_message.message.empty()) {
-    return std::nullopt;
-  }
-
-  return element->second.custom_message.message;
-}
-
-std::optional<GURL> AnalysisServiceSettings::GetLearnMoreUrl(
-    const std::string& tag) {
-  const auto& element = tags_.find(tag);
-
-  if (!IsValid() || element == tags_.end() ||
-      element->second.custom_message.learn_more_url.is_empty()) {
-    return std::nullopt;
-  }
-
-  return element->second.custom_message.learn_more_url;
-}
-
-bool AnalysisServiceSettings::GetBypassJustificationRequired(
-    const std::string& tag) {
-  return tags_.find(tag) != tags_.end() && tags_.at(tag).requires_justification;
-}
-
-bool AnalysisServiceSettings::is_cloud_analysis() const {
-  return analysis_config_ && analysis_config_->url != nullptr;
-}
-
-bool AnalysisServiceSettings::is_local_analysis() const {
-  return analysis_config_ && analysis_config_->local_path != nullptr;
-}
-
 #if BUILDFLAG(IS_CHROMEOS)
 void AnalysisServiceSettings::AddSourceDestinationSettings(
     const base::Value::Dict& source_destination_settings_value,
@@ -295,58 +214,6 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
-std::map<std::string, TagSettings> AnalysisServiceSettings::GetTags(
-    const std::set<base::MatcherStringPattern::ID>& matches) const {
-  std::set<std::string> enable_tags;
-  std::set<std::string> disable_tags;
-  for (const base::MatcherStringPattern::ID match : matches) {
-    // Enabled patterns need to be checked first, otherwise they always match
-    // the first disabled pattern.
-    bool enable = true;
-    auto maybe_pattern_setting =
-        GetPatternSettings(enabled_patterns_settings_, match);
-    if (!maybe_pattern_setting.has_value()) {
-      maybe_pattern_setting =
-          GetPatternSettings(disabled_patterns_settings_, match);
-      enable = false;
-    }
-
-    DCHECK(maybe_pattern_setting.has_value());
-    auto tags = std::move(maybe_pattern_setting.value().tags);
-    if (enable) {
-      enable_tags.insert(tags.begin(), tags.end());
-    } else {
-      disable_tags.insert(tags.begin(), tags.end());
-    }
-  }
-
-  for (const std::string& tag_to_disable : disable_tags) {
-    enable_tags.erase(tag_to_disable);
-  }
-
-  std::map<std::string, TagSettings> output;
-  for (const std::string& tag : enable_tags) {
-    if (tags_.count(tag)) {
-      output[tag] = tags_.at(tag);
-    } else {
-      output[tag] = TagSettings();
-    }
-  }
-
-  return output;
-}
-
-bool AnalysisServiceSettings::IsValid() const {
-  // The settings are invalid if no provider was given.
-  if (!analysis_config_) {
-    return false;
-  }
-
-  // The settings are invalid if no enabled pattern(s) exist since that would
-  // imply no URL can ever have an analysis.
-  return !enabled_patterns_settings_.empty();
-}
-
 AnalysisServiceSettings::AnalysisServiceSettings(AnalysisServiceSettings&&) =
     default;
 AnalysisServiceSettings& AnalysisServiceSettings::operator=(
diff --git a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.h b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.h
index 1bdb95d..e404ee5 100644
--- a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.h
+++ b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.h
@@ -60,33 +60,7 @@
       DataRegion data_region) const;
 #endif
 
-  // TODO(crbug.com/444237640): Move getter methods to the base class.
-  // Get the block_until_verdict setting if the settings are valid.
-  bool ShouldBlockUntilVerdict() const;
-
-  // Get the default_action setting if the settings are valid.
-  bool ShouldBlockByDefault() const;
-
-  // Get the custom message/learn more URL. Returns std::nullopt if the
-  // settings are invalid or if the message/URL are empty.
-  std::optional<std::u16string> GetCustomMessage(const std::string& tag);
-  std::optional<GURL> GetLearnMoreUrl(const std::string& tag);
-  bool GetBypassJustificationRequired(const std::string& tag);
-
-  std::string service_provider_name() const { return service_provider_name_; }
-
-  // Helpers for convenient check of the underlying variant.
-  bool is_cloud_analysis() const;
-  bool is_local_analysis() const;
-
-  const AnalysisConfig* GetAnalysisConfig() const { return analysis_config_; }
-
  private:
-  // Accessors for the pattern setting maps.
-  static std::optional<URLPatternSettings> GetPatternSettings(
-      const PatternSettings& patterns,
-      base::MatcherStringPattern::ID match);
-
   // Helper methods for parsing the raw policy settings input
 #if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
   void ParseVerificationSignatures(const base::Value::Dict& settings_dict);
@@ -97,10 +71,6 @@
       std::map<std::string, TagSettings> tags,
       DataRegion data_region) const;
 
-  // Returns true if the settings were initialized correctly. If this returns
-  // false, then GetAnalysisSettings will always return std::nullopt.
-  bool IsValid() const;
-
 #if BUILDFLAG(IS_CHROMEOS)
   // Updates the states of `source_destination_matcher_`,
   // `enabled_patterns_settings_` and/or `disabled_patterns_settings_` from a
@@ -111,11 +81,6 @@
       base::MatcherStringPattern::ID* id) override;
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
-  // Return tags found in |enabled_patterns_settings| corresponding to the
-  // matches while excluding the ones in |disable_patterns_settings|.
-  std::map<std::string, TagSettings> GetTags(
-      const std::set<base::MatcherStringPattern::ID>& matches) const;
-
 #if BUILDFLAG(IS_CHROMEOS)
   // A matcher to identify matching pairs of sources and destinations.
   // Set for ChromeOS' OnFileTransferEnterpriseConnector.
diff --git a/chrome/browser/enterprise/connectors/reporting/reporting_event_router_unittest.cc b/chrome/browser/enterprise/connectors/reporting/reporting_event_router_unittest.cc
index b2e18f8..fb0737d45 100644
--- a/chrome/browser/enterprise/connectors/reporting/reporting_event_router_unittest.cc
+++ b/chrome/browser/enterprise/connectors/reporting/reporting_event_router_unittest.cc
@@ -1400,7 +1400,7 @@
       /*enabled_opt_in_events=*/{});
 
   data_controls::Verdict::TriggeredRules triggered_rules = {
-      {0, {"1", "rule_1_name"}}};
+      {{0, true}, {"1", "rule_1_name"}}};
   test::EventReportValidator validator(client_.get());
   base::RunLoop run_loop;
   validator.SetDoneClosure(run_loop.QuitClosure());
diff --git a/chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.cc b/chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.cc
index bd9b256d..3455b96 100644
--- a/chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.cc
+++ b/chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.cc
@@ -1019,18 +1019,28 @@
           rule.GetDict().FindString(kKeyTriggeredRuleName);
       ASSERT_TRUE(name);
 
-      ASSERT_TRUE(data_controls_triggered_rules_.count(i));
-      ASSERT_EQ(data_controls_triggered_rules_[i].rule_name, *name);
+      // There should be a rule with the same index as in `triggered_rules`, but
+      // `data_controls_triggered_rules_` might be tracking it internally as a
+      // profile rule or machine rule so we need to check with two different
+      // keys.
+      data_controls::Verdict::TriggeredRule expected_rule;
+      if (data_controls_triggered_rules_.count({i, true})) {
+        expected_rule = data_controls_triggered_rules_[{i, true}];
+      } else if (data_controls_triggered_rules_.count({i, false})) {
+        expected_rule = data_controls_triggered_rules_[{i, false}];
+      } else {
+        NOTREACHED();
+      }
 
       std::optional<int> id = rule.GetDict().FindInt(kKeyTriggeredRuleId);
       if (id) {
         int expected_rule_id = 0;
-        ASSERT_TRUE(base::StringToInt(data_controls_triggered_rules_[i].rule_id,
-                                      &expected_rule_id));
+        ASSERT_TRUE(
+            base::StringToInt(expected_rule.rule_id, &expected_rule_id));
         ASSERT_EQ(expected_rule_id, *id);
       } else {
-        ASSERT_TRUE(data_controls_triggered_rules_[i].rule_id.empty())
-            << " Got rule_id " << data_controls_triggered_rules_[i].rule_id
+        ASSERT_TRUE(expected_rule.rule_id.empty())
+            << " Got rule_id " << expected_rule.rule_id
             << " instead of nothing.";
       }
 
diff --git a/chrome/browser/enterprise/data_controls/chrome_rules_service_unittest.cc b/chrome/browser/enterprise/data_controls/chrome_rules_service_unittest.cc
index b75e810..a50e452 100644
--- a/chrome/browser/enterprise/data_controls/chrome_rules_service_unittest.cc
+++ b/chrome/browser/enterprise/data_controls/chrome_rules_service_unittest.cc
@@ -20,7 +20,10 @@
 
 namespace {
 
-constexpr size_t kFirstRuleIndex = 0;
+constexpr Verdict::TriggeredRuleKey kFirstRuleKey = {
+    .index = 0,
+    .machine_scope = true,
+};
 constexpr char kFirstRuleID[] = "1234";
 
 class DataControlsRulesServiceTest : public testing::Test {
@@ -100,18 +103,18 @@
   void ExpectBlockVerdict(Verdict verdict) const {
     ASSERT_EQ(verdict.level(), Rule::Level::kBlock);
     EXPECT_EQ(verdict.triggered_rules().size(), 1u);
-    EXPECT_TRUE(verdict.triggered_rules().count(kFirstRuleIndex));
-    EXPECT_EQ(verdict.triggered_rules().at(kFirstRuleIndex).rule_name, "block");
-    EXPECT_EQ(verdict.triggered_rules().at(kFirstRuleIndex).rule_id,
+    EXPECT_TRUE(verdict.triggered_rules().count(kFirstRuleKey));
+    EXPECT_EQ(verdict.triggered_rules().at(kFirstRuleKey).rule_name, "block");
+    EXPECT_EQ(verdict.triggered_rules().at(kFirstRuleKey).rule_id,
               kFirstRuleID);
   }
 
   void ExpectWarnVerdict(Verdict verdict) const {
     ASSERT_EQ(verdict.level(), Rule::Level::kWarn);
     EXPECT_EQ(verdict.triggered_rules().size(), 1u);
-    EXPECT_TRUE(verdict.triggered_rules().count(kFirstRuleIndex));
-    EXPECT_EQ(verdict.triggered_rules().at(kFirstRuleIndex).rule_name, "warn");
-    EXPECT_EQ(verdict.triggered_rules().at(kFirstRuleIndex).rule_id,
+    EXPECT_TRUE(verdict.triggered_rules().count(kFirstRuleKey));
+    EXPECT_EQ(verdict.triggered_rules().at(kFirstRuleKey).rule_name, "warn");
+    EXPECT_EQ(verdict.triggered_rules().at(kFirstRuleKey).rule_id,
               kFirstRuleID);
   }
 
diff --git a/chrome/browser/enterprise/data_controls/reporting_unittest.cc b/chrome/browser/enterprise/data_controls/reporting_unittest.cc
index 125ec5c5..62bfa7d8 100644
--- a/chrome/browser/enterprise/data_controls/reporting_unittest.cc
+++ b/chrome/browser/enterprise/data_controls/reporting_unittest.cc
@@ -191,11 +191,11 @@
               .size = 1234,
               .format_type = ui::ClipboardFormatType::PlainTextType(),
           }),
-      Verdict::Warn({{0, {"1", "rule_1_name"}}}));
+      Verdict::Warn({{{0, true}, {"1", "rule_1_name"}}}));
   router->ReportPasteWarningBypassed(
       ChromeClipboardContext(managed_endpoint(GURL(kGoogleUrl)),
                              managed_endpoint(GURL(kChromiumUrl)), {}),
-      Verdict::Warn({{0, {"1", "rule_1_name"}}}));
+      Verdict::Warn({{{0, true}, {"1", "rule_1_name"}}}));
   router->ReportCopy(
       ChromeClipboardContext(
           managed_endpoint(GURL(kChromiumUrl)),
@@ -203,10 +203,10 @@
               .size = 1234,
               .format_type = ui::ClipboardFormatType::PlainTextType(),
           }),
-      Verdict::Warn({{0, {"1", "rule_1_name"}}}));
+      Verdict::Warn({{{0, true}, {"1", "rule_1_name"}}}));
   router->ReportCopyWarningBypassed(
       ChromeClipboardContext(managed_endpoint(GURL(kChromiumUrl)), {}),
-      Verdict::Warn({{0, {"1", "rule_1_name"}}}));
+      Verdict::Warn({{{0, true}, {"1", "rule_1_name"}}}));
 
   // This wait call is necessary since all the "Report*" calls trigger async
   // code, so we need to wait a bit so the "validator.ExpectNoReport();" call is
@@ -229,11 +229,11 @@
               .size = 1234,
               .format_type = ui::ClipboardFormatType::PlainTextType(),
           }),
-      Verdict::Warn({{0, {"1", "rule_1_name"}}}));
+      Verdict::Warn({{{0, true}, {"1", "rule_1_name"}}}));
   router->ReportPasteWarningBypassed(
       ChromeClipboardContext(managed_endpoint(GURL(kGoogleUrl)),
                              unmanaged_endpoint(GURL(kChromiumUrl)), {}),
-      Verdict::Warn({{0, {"1", "rule_1_name"}}}));
+      Verdict::Warn({{{0, true}, {"1", "rule_1_name"}}}));
   router->ReportCopy(
       ChromeClipboardContext(
           unmanaged_endpoint(GURL(kChromiumUrl)),
@@ -241,10 +241,10 @@
               .size = 1234,
               .format_type = ui::ClipboardFormatType::PlainTextType(),
           }),
-      Verdict::Warn({{0, {"1", "rule_1_name"}}}));
+      Verdict::Warn({{{0, true}, {"1", "rule_1_name"}}}));
   router->ReportCopyWarningBypassed(
       ChromeClipboardContext(unmanaged_endpoint(GURL(kChromiumUrl)), {}),
-      Verdict::Warn({{0, {"1", "rule_1_name"}}}));
+      Verdict::Warn({{{0, true}, {"1", "rule_1_name"}}}));
 
   // This wait call is necessary since all the "Report*" calls trigger async
   // code, so we need to wait a bit so the "validator.ExpectNoReport();" call is
@@ -334,7 +334,7 @@
 }
 
 TEST_F(DataControlsReportingTest, PasteInManagedProfile_OSClipboardSource) {
-  Verdict::TriggeredRules triggered_rules = {{0, {"1", "rule_1_name"}}};
+  Verdict::TriggeredRules triggered_rules = {{{0, true}, {"1", "rule_1_name"}}};
   auto validator = helper_->CreateValidator();
   base::RunLoop validator_run_loop;
   validator.SetDoneClosure(validator_run_loop.QuitClosure());
@@ -374,7 +374,7 @@
 
 TEST_F(DataControlsReportingTest,
        PasteInManagedProfile_IncognitoOSClipboardSource) {
-  Verdict::TriggeredRules triggered_rules = {{0, {"1", "rule_1_name"}}};
+  Verdict::TriggeredRules triggered_rules = {{{0, true}, {"1", "rule_1_name"}}};
   auto validator = helper_->CreateValidator();
   base::RunLoop validator_run_loop;
   validator.SetDoneClosure(validator_run_loop.QuitClosure());
@@ -413,7 +413,7 @@
 }
 
 TEST_F(DataControlsReportingTest, PasteInManagedProfile_ManagedSourceProfile) {
-  Verdict::TriggeredRules triggered_rules = {{0, {"1", "rule_1_name"}}};
+  Verdict::TriggeredRules triggered_rules = {{{0, true}, {"1", "rule_1_name"}}};
   auto validator = helper_->CreateValidator();
   base::RunLoop validator_run_loop;
   validator.SetDoneClosure(validator_run_loop.QuitClosure());
@@ -453,8 +453,8 @@
 TEST_F(DataControlsReportingTest,
        PasteInManagedProfile_IncognitoManagedSourceProfile) {
   Verdict::TriggeredRules triggered_rules = {
-      {0, {"1", "rule_1_name"}},
-      {1, {"2", "rule_2_name"}},
+      {{0, true}, {"1", "rule_1_name"}},
+      {{1, true}, {"2", "rule_2_name"}},
   };
   auto validator = helper_->CreateValidator();
   base::RunLoop validator_run_loop;
@@ -494,7 +494,7 @@
 
 TEST_F(DataControlsReportingTest,
        PasteInManagedProfile_UnmanagedSourceProfile) {
-  Verdict::TriggeredRules triggered_rules = {{0, {"1", "rule_1_name"}}};
+  Verdict::TriggeredRules triggered_rules = {{{0, true}, {"1", "rule_1_name"}}};
   auto validator = helper_->CreateValidator();
   base::RunLoop validator_run_loop;
   validator.SetDoneClosure(validator_run_loop.QuitClosure());
@@ -538,7 +538,7 @@
   managed_profile_->GetPrefs()->SetInteger(kDataControlsRulesScopePref,
                                            policy::POLICY_SCOPE_MACHINE);
 
-  Verdict::TriggeredRules triggered_rules = {{0, {"1", "rule_1_name"}}};
+  Verdict::TriggeredRules triggered_rules = {{{0, true}, {"1", "rule_1_name"}}};
   auto validator = helper_->CreateValidator();
   base::RunLoop validator_run_loop;
   validator.SetDoneClosure(validator_run_loop.QuitClosure());
@@ -575,7 +575,7 @@
 }
 
 TEST_F(DataControlsReportingTest, CopyInManagedProfile) {
-  Verdict::TriggeredRules triggered_rules = {{0, {"1", "rule_1_name"}}};
+  Verdict::TriggeredRules triggered_rules = {{{0, true}, {"1", "rule_1_name"}}};
   auto* router =
       enterprise_connectors::ReportingEventRouterFactory::GetForBrowserContext(
           managed_profile_);
diff --git a/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_browsertest.cc b/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_browsertest.cc
index ee692ff..39f993eec 100644
--- a/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_browsertest.cc
+++ b/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils_browsertest.cc
@@ -20,14 +20,17 @@
 #include "chrome/browser/enterprise/data_controls/desktop_data_controls_dialog.h"
 #include "chrome/browser/enterprise/data_controls/desktop_data_controls_dialog_test_helper.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_test_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "components/enterprise/connectors/core/features.h"
+#include "components/enterprise/connectors/core/reporting_test_utils.h"
 #include "components/enterprise/data_controls/core/browser/test_utils.h"
 #include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
 #include "components/safe_browsing/core/common/features.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/test_utils.h"
 #include "ui/base/clipboard/clipboard_metadata.h"
 #include "ui/base/clipboard/clipboard_monitor.h"
 #include "ui/base/clipboard/test/test_clipboard.h"
@@ -126,6 +129,16 @@
                                       *contents()->GetPrimaryMainFrame());
   }
 
+  Profile* CreateAdditionalProfile() {
+    ProfileManager* profile_manager = g_browser_process->profile_manager();
+
+    base::FilePath new_path =
+        profile_manager->GenerateNextProfileDirectoryPath();
+    Profile& profile =
+        profiles::testing::CreateProfileSync(profile_manager, new_path);
+    return &profile;
+  }
+
  protected:
   std::unique_ptr<enterprise_connectors::test::EventReportValidatorHelper>
       event_report_validator_helper_;
@@ -242,7 +255,7 @@
           return &set;
         }(),
         /*expected_trigger=*/"WEB_CONTENT_UPLOAD",
-        /*triggered_rules=*/{{0, {"222", "rule_name"}}},
+        /*triggered_rules=*/{{{0, machine_scope()}, {"222", "rule_name"}}},
         /*expected_result=*/"EVENT_RESULT_BLOCKED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -334,7 +347,7 @@
           return &set;
         }(),
         /*expected_trigger=*/"WEB_CONTENT_UPLOAD",
-        /*triggered_rules=*/{{0, {"333", "warn_rule_name"}}},
+        /*triggered_rules=*/{{{0, machine_scope()}, {"333", "warn_rule_name"}}},
         /*expected_result=*/"EVENT_RESULT_WARNED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -420,7 +433,7 @@
           return &set;
         }(),
         /*expected_trigger=*/"WEB_CONTENT_UPLOAD",
-        /*triggered_rules=*/{{0, {"333", "warn_rule_name"}}},
+        /*triggered_rules=*/{{{0, machine_scope()}, {"333", "warn_rule_name"}}},
         /*expected_result=*/"EVENT_RESULT_BYPASSED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -493,7 +506,8 @@
           return &set;
         }(),
         /*expected_trigger=*/"WEB_CONTENT_UPLOAD",
-        /*triggered_rules=*/{{0, {"1416", "warn rule name"}}},
+        /*triggered_rules=*/
+        {{{0, machine_scope()}, {"1416", "warn rule name"}}},
         /*expected_result=*/"EVENT_RESULT_WARNED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -546,10 +560,10 @@
 #if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_P(DataControlsClipboardUtilsBrowserTest,
                        PasteBlockedByDataControls_SourceRule) {
-  auto event_validator = event_report_validator_helper_->CreateValidator();
-  event_validator.ExpectNoReport();
-
-  data_controls::SetDataControls(browser()->profile()->GetPrefs(), {R"({
+  // By making a new profile for this test, we ensure we can prevent pasting to
+  // it by having the rule set in the source profile only.
+  Profile* source_profile = CreateAdditionalProfile();
+  data_controls::SetDataControls(source_profile->GetPrefs(), {R"({
                                    "name": "report_rule_name",
                                    "rule_id": "4321",
                                    "destinations": {
@@ -560,38 +574,85 @@
                                    ]
                                  })"},
                                  machine_scope());
+
   data_controls::DesktopDataControlsDialogTestHelper helper(
       data_controls::DataControlsDialog::Type::kClipboardPasteBlock);
 
-  // By making a new profile for this test, we ensure we can prevent pasting to
-  // it by having the rule set in the source profile.
-  std::unique_ptr<Profile> destination_profile;
-  {
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    destination_profile = Profile::CreateProfile(
-        g_browser_process->profile_manager()->user_data_dir().Append(
-            FILE_PATH_LITERAL("DC Test Profile")),
-        /*delegate=*/nullptr, Profile::CreateMode::kSynchronous);
+  base::RunLoop report_run_loop;
+  auto event_validator = event_report_validator_helper_->CreateValidator();
+
+  // The event should only be reported when the policies are set at the machine
+  // level as we would otherwise be reporting from a different unmanaged or
+  // unaffiliated profile.
+  if (machine_scope()) {
+    event_validator.SetDoneClosure(report_run_loop.QuitClosure());
+    if (use_proto_format()) {
+      chrome::cros::reporting::proto::DlpSensitiveDataEvent expected_event;
+      expected_event.set_url(test_url_1());
+      expected_event.set_tab_url(test_url_1());
+      expected_event.set_source("OTHER_PROFILE");
+      expected_event.set_destination(test_url_1());
+      expected_event.set_content_type("text/plain");
+      expected_event.set_content_size(1234);
+      expected_event.set_trigger(
+          chrome::cros::reporting::proto::DataTransferEventTrigger::
+              WEB_CONTENT_UPLOAD);
+      expected_event.set_event_result(
+          chrome::cros::reporting::proto::EventResult::EVENT_RESULT_BLOCKED);
+
+      ::chrome::cros::reporting::proto::TriggeredRuleInfo triggered_rule;
+      triggered_rule.set_rule_id(4321);
+      triggered_rule.set_rule_name("report_rule_name");
+
+      *expected_event.add_triggered_rule_info() = triggered_rule;
+      expected_event.set_profile_identifier(
+          browser()->profile()->GetPath().AsUTF8Unsafe());
+      expected_event.set_profile_user_name(kUserName);
+
+      event_validator.ExpectSensitiveDataEvent(std::move(expected_event));
+    } else {
+      event_validator.ExpectDataControlsSensitiveDataEvent(
+          /*expected_url=*/test_url_1(),
+          /*expected_tab_url=*/test_url_1(),
+          /*expected_source=*/"OTHER_PROFILE",
+          /*expected_destination=*/test_url_1(),
+          /*expected_mimetypes=*/
+          []() {
+            static std::set<std::string> set = {"text/plain"};
+            return &set;
+          }(),
+          /*expected_trigger=*/"WEB_CONTENT_UPLOAD",
+          /*triggered_rules=*/
+          {{{0, machine_scope()}, {"4321", "report_rule_name"}}},
+          /*expected_result=*/"EVENT_RESULT_BLOCKED",
+          /*expected_profile_username=*/kUserName,
+          /*expected_profile_identifier=*/
+          browser()->profile()->GetPath().AsUTF8Unsafe(),
+          /*expected_content_size=*/1234);
+    }
+  } else {
+    event_validator.ExpectNoReport();
   }
 
   base::test::TestFuture<std::optional<content::ClipboardPasteData>> future;
   PasteIfAllowedByPolicy(
-      /*source=*/content::ClipboardEndpoint(
-          ui::DataTransferEndpoint(GURL("https://foo.com")),
-          base::BindLambdaForTesting(
-              [this]() { return contents()->GetBrowserContext(); }),
-          *contents()->GetPrimaryMainFrame()),
-      /*destination=*/
+      /*source=*/
       content::ClipboardEndpoint(
-          ui::DataTransferEndpoint(GURL("https://google.com")),
+          ui::DataTransferEndpoint(GURL(test_url_0())),
           base::BindLambdaForTesting(
-              [&destination_profile]() -> content::BrowserContext* {
-                return destination_profile.get();
+              [&source_profile]() -> content::BrowserContext* {
+                return source_profile;
               }),
           *contents()->GetPrimaryMainFrame()),
+      /*destination=*/
+      content::ClipboardEndpoint(ui::DataTransferEndpoint(GURL(test_url_1())),
+                                 base::BindLambdaForTesting([this]() {
+                                   return contents()->GetBrowserContext();
+                                 }),
+                                 *contents()->GetPrimaryMainFrame()),
       /*metadata=*/
-      {.size = 1234}, MakeClipboardPasteData("text", "image", {}),
-      future.GetCallback());
+      {.size = 1234, .format_type = ui::ClipboardFormatType::PlainTextType()},
+      MakeClipboardPasteData("text", "image", {}), future.GetCallback());
 
   auto paste_data = future.Get();
   EXPECT_FALSE(paste_data);
@@ -599,14 +660,18 @@
   helper.WaitForDialogToInitialize();
   helper.CloseDialogWithoutBypass();
   helper.WaitForDialogToClose();
+
+  if (machine_scope()) {
+    report_run_loop.Run();
+  }
 }
 
 IN_PROC_BROWSER_TEST_P(DataControlsClipboardUtilsBrowserTest,
                        PasteWarnedByDataControls_BypassedSourceRule) {
-  auto event_validator = event_report_validator_helper_->CreateValidator();
-  event_validator.ExpectNoReport();
-
-  data_controls::SetDataControls(browser()->profile()->GetPrefs(), {R"({
+  // By making a new profile for this test, we ensure we can prevent pasting to
+  // it by having the rule set in the source profile only.
+  Profile* source_profile = CreateAdditionalProfile();
+  data_controls::SetDataControls(source_profile->GetPrefs(), {R"({
                                    "name": "report_rule_name",
                                    "rule_id": "6543",
                                    "destinations": {
@@ -617,42 +682,145 @@
                                    ]
                                  })"},
                                  machine_scope());
+
   data_controls::DesktopDataControlsDialogTestHelper helper(
       data_controls::DataControlsDialog::Type::kClipboardPasteWarn);
 
-  // By making a new profile for this test, we ensure we can prevent pasting to
-  // it by having the rule set in the source profile.
-  std::unique_ptr<Profile> destination_profile;
-  {
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    destination_profile = Profile::CreateProfile(
-        g_browser_process->profile_manager()->user_data_dir().Append(
-            FILE_PATH_LITERAL("DC Test Profile")),
-        /*delegate=*/nullptr, Profile::CreateMode::kSynchronous);
+  base::RunLoop run_loop_warn;
+  auto event_validator = event_report_validator_helper_->CreateValidator();
+
+  // The event should only be reported when the policies are set at the machine
+  // level as we would otherwise be reporting from a different unmanaged or
+  // unaffiliated profile.
+  if (machine_scope()) {
+    event_validator.SetDoneClosure(run_loop_warn.QuitClosure());
+    if (use_proto_format()) {
+      chrome::cros::reporting::proto::DlpSensitiveDataEvent expected_event;
+      expected_event.set_url(test_url_1());
+      expected_event.set_tab_url(test_url_1());
+      expected_event.set_source("OTHER_PROFILE");
+      expected_event.set_destination(test_url_1());
+      expected_event.set_content_type("text/plain");
+      expected_event.set_content_size(1234);
+      expected_event.set_trigger(
+          chrome::cros::reporting::proto::DataTransferEventTrigger::
+              WEB_CONTENT_UPLOAD);
+      expected_event.set_event_result(
+          chrome::cros::reporting::proto::EventResult::EVENT_RESULT_WARNED);
+
+      ::chrome::cros::reporting::proto::TriggeredRuleInfo triggered_rule;
+      triggered_rule.set_rule_id(6543);
+      triggered_rule.set_rule_name("report_rule_name");
+
+      *expected_event.add_triggered_rule_info() = triggered_rule;
+      expected_event.set_profile_identifier(
+          browser()->profile()->GetPath().AsUTF8Unsafe());
+      expected_event.set_profile_user_name(kUserName);
+
+      event_validator.ExpectSensitiveDataEvent(std::move(expected_event));
+    } else {
+      event_validator.ExpectDataControlsSensitiveDataEvent(
+          /*expected_url=*/test_url_1(),
+          /*expected_tab_url=*/test_url_1(),
+          /*expected_source=*/"OTHER_PROFILE",
+          /*expected_destination=*/test_url_1(),
+          /*expected_mimetypes=*/
+          []() {
+            static std::set<std::string> set = {"text/plain"};
+            return &set;
+          }(),
+          /*expected_trigger=*/"WEB_CONTENT_UPLOAD",
+          /*triggered_rules=*/
+          {{{0, machine_scope()}, {"6543", "report_rule_name"}}},
+          /*expected_result=*/"EVENT_RESULT_WARNED",
+          /*expected_profile_username=*/kUserName,
+          /*expected_profile_identifier=*/
+          browser()->profile()->GetPath().AsUTF8Unsafe(),
+          /*expected_content_size=*/1234);
+    }
+  } else {
+    event_validator.ExpectNoReport();
   }
 
   base::test::TestFuture<std::optional<content::ClipboardPasteData>> future;
   PasteIfAllowedByPolicy(
-      /*source=*/content::ClipboardEndpoint(
-          ui::DataTransferEndpoint(GURL("https://foo.com")),
-          base::BindLambdaForTesting(
-              [this]() { return contents()->GetBrowserContext(); }),
-          *contents()->GetPrimaryMainFrame()),
-      /*destination=*/
+      /*source=*/
       content::ClipboardEndpoint(
-          ui::DataTransferEndpoint(GURL("https://google.com")),
+          ui::DataTransferEndpoint(GURL(test_url_0())),
           base::BindLambdaForTesting(
-              [&destination_profile]() -> content::BrowserContext* {
-                return destination_profile.get();
+              [&source_profile]() -> content::BrowserContext* {
+                return source_profile;
               }),
           *contents()->GetPrimaryMainFrame()),
-      /*metadata=*/{.size = 1234}, MakeClipboardPasteData("text", "image", {}),
-      future.GetCallback());
+      /*destination=*/
+      content::ClipboardEndpoint(ui::DataTransferEndpoint(GURL(test_url_1())),
+                                 base::BindLambdaForTesting([this]() {
+                                   return contents()->GetBrowserContext();
+                                 }),
+                                 *contents()->GetPrimaryMainFrame()),
+      /*metadata=*/
+      {.size = 1234, .format_type = ui::ClipboardFormatType::PlainTextType()},
+      MakeClipboardPasteData("text", "image", {}), future.GetCallback());
 
   // The dialog will stay up until a user action dismisses it, so `future`
   // shouldn't be ready yet.
   EXPECT_FALSE(future.IsReady());
 
+  base::RunLoop run_loop_bypass;
+  if (machine_scope()) {
+    run_loop_warn.Run();
+
+    // The first warn event should already be reported before the dialog has
+    // been initialized, so it can be reassigned so that the bypass event can be
+    // validated.
+    event_validator = event_report_validator_helper_->CreateValidator();
+    event_validator.SetDoneClosure(run_loop_bypass.QuitClosure());
+    if (use_proto_format()) {
+      chrome::cros::reporting::proto::DlpSensitiveDataEvent expected_event;
+      expected_event.set_url(test_url_1());
+      expected_event.set_tab_url(test_url_1());
+      expected_event.set_source("OTHER_PROFILE");
+      expected_event.set_destination(test_url_1());
+      expected_event.set_content_type("text/plain");
+      expected_event.set_content_size(1234);
+      expected_event.set_trigger(
+          chrome::cros::reporting::proto::DataTransferEventTrigger::
+              WEB_CONTENT_UPLOAD);
+      expected_event.set_event_result(
+          chrome::cros::reporting::proto::EventResult::EVENT_RESULT_BYPASSED);
+
+      ::chrome::cros::reporting::proto::TriggeredRuleInfo triggered_rule;
+      triggered_rule.set_rule_id(6543);
+      triggered_rule.set_rule_name("report_rule_name");
+
+      *expected_event.add_triggered_rule_info() = triggered_rule;
+      expected_event.set_profile_identifier(
+          browser()->profile()->GetPath().AsUTF8Unsafe());
+      expected_event.set_profile_user_name(kUserName);
+
+      event_validator.ExpectSensitiveDataEvent(std::move(expected_event));
+    } else {
+      event_validator.ExpectDataControlsSensitiveDataEvent(
+          /*expected_url=*/test_url_1(),
+          /*expected_tab_url=*/test_url_1(),
+          /*expected_source=*/"OTHER_PROFILE",
+          /*expected_destination=*/test_url_1(),
+          /*expected_mimetypes=*/
+          []() {
+            static std::set<std::string> set = {"text/plain"};
+            return &set;
+          }(),
+          /*expected_trigger=*/"WEB_CONTENT_UPLOAD",
+          /*triggered_rules=*/
+          {{{0, machine_scope()}, {"6543", "report_rule_name"}}},
+          /*expected_result=*/"EVENT_RESULT_BYPASSED",
+          /*expected_profile_username=*/kUserName,
+          /*expected_profile_identifier=*/
+          browser()->profile()->GetPath().AsUTF8Unsafe(),
+          /*expected_content_size=*/1234);
+    }
+  }
+
   helper.BypassWarning();
   helper.WaitForDialogToClose();
 
@@ -661,14 +829,18 @@
   EXPECT_EQ(paste_data->text, u"text");
   EXPECT_EQ(std::string(paste_data->png.begin(), paste_data->png.end()),
             "image");
+
+  if (machine_scope()) {
+    run_loop_bypass.Run();
+  }
 }
 
 IN_PROC_BROWSER_TEST_P(DataControlsClipboardUtilsBrowserTest,
                        PasteWarnedByDataControls_CanceledSourceRule) {
-  auto event_validator = event_report_validator_helper_->CreateValidator();
-  event_validator.ExpectNoReport();
-
-  data_controls::SetDataControls(browser()->profile()->GetPrefs(), {R"({
+  // By making a new profile for this test, we ensure we can prevent pasting to
+  // it by having the rule set in the source profile only.
+  Profile* source_profile = CreateAdditionalProfile();
+  data_controls::SetDataControls(source_profile->GetPrefs(), {R"({
                                    "name": "report_rule_name",
                                    "rule_id": "7654",
                                    "destinations": {
@@ -679,37 +851,85 @@
                                    ]
                                  })"},
                                  machine_scope());
+
   data_controls::DesktopDataControlsDialogTestHelper helper(
       data_controls::DataControlsDialog::Type::kClipboardPasteWarn);
 
-  // By making a new profile for this test, we ensure we can prevent pasting to
-  // it by having the rule set in the source profile.
-  std::unique_ptr<Profile> destination_profile;
-  {
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    destination_profile = Profile::CreateProfile(
-        g_browser_process->profile_manager()->user_data_dir().Append(
-            FILE_PATH_LITERAL("DC Test Profile")),
-        /*delegate=*/nullptr, Profile::CreateMode::kSynchronous);
+  base::RunLoop report_run_loop;
+  auto event_validator = event_report_validator_helper_->CreateValidator();
+
+  // The event should only be reported when the policies are set at the machine
+  // level as we would otherwise be reporting from a different unmanaged or
+  // unaffiliated profile.
+  if (machine_scope()) {
+    event_validator.SetDoneClosure(report_run_loop.QuitClosure());
+    if (use_proto_format()) {
+      chrome::cros::reporting::proto::DlpSensitiveDataEvent expected_event;
+      expected_event.set_url(test_url_1());
+      expected_event.set_tab_url(test_url_1());
+      expected_event.set_source("OTHER_PROFILE");
+      expected_event.set_destination(test_url_1());
+      expected_event.set_content_type("text/plain");
+      expected_event.set_content_size(1234);
+      expected_event.set_trigger(
+          chrome::cros::reporting::proto::DataTransferEventTrigger::
+              WEB_CONTENT_UPLOAD);
+      expected_event.set_event_result(
+          chrome::cros::reporting::proto::EventResult::EVENT_RESULT_WARNED);
+
+      ::chrome::cros::reporting::proto::TriggeredRuleInfo triggered_rule;
+      triggered_rule.set_rule_id(7654);
+      triggered_rule.set_rule_name("report_rule_name");
+
+      *expected_event.add_triggered_rule_info() = triggered_rule;
+      expected_event.set_profile_identifier(
+          browser()->profile()->GetPath().AsUTF8Unsafe());
+      expected_event.set_profile_user_name(kUserName);
+
+      event_validator.ExpectSensitiveDataEvent(std::move(expected_event));
+    } else {
+      event_validator.ExpectDataControlsSensitiveDataEvent(
+          /*expected_url=*/test_url_1(),
+          /*expected_tab_url=*/test_url_1(),
+          /*expected_source=*/"OTHER_PROFILE",
+          /*expected_destination=*/test_url_1(),
+          /*expected_mimetypes=*/
+          []() {
+            static std::set<std::string> set = {"text/plain"};
+            return &set;
+          }(),
+          /*expected_trigger=*/"WEB_CONTENT_UPLOAD",
+          /*triggered_rules=*/
+          {{{0, machine_scope()}, {"7654", "report_rule_name"}}},
+          /*expected_result=*/"EVENT_RESULT_WARNED",
+          /*expected_profile_username=*/kUserName,
+          /*expected_profile_identifier=*/
+          browser()->profile()->GetPath().AsUTF8Unsafe(),
+          /*expected_content_size=*/1234);
+    }
+  } else {
+    event_validator.ExpectNoReport();
   }
 
   base::test::TestFuture<std::optional<content::ClipboardPasteData>> future;
   PasteIfAllowedByPolicy(
-      /*source=*/content::ClipboardEndpoint(
-          ui::DataTransferEndpoint(GURL("https://foo.com")),
-          base::BindLambdaForTesting(
-              [this]() { return contents()->GetBrowserContext(); }),
-          *contents()->GetPrimaryMainFrame()),
-      /*destination=*/
+      /*source=*/
       content::ClipboardEndpoint(
-          ui::DataTransferEndpoint(GURL("https://google.com")),
+          ui::DataTransferEndpoint(GURL(test_url_0())),
           base::BindLambdaForTesting(
-              [&destination_profile]() -> content::BrowserContext* {
-                return destination_profile.get();
+              [&source_profile]() -> content::BrowserContext* {
+                return source_profile;
               }),
           *contents()->GetPrimaryMainFrame()),
-      /*metadata=*/{.size = 1234}, MakeClipboardPasteData("text", "image", {}),
-      future.GetCallback());
+      /*destination=*/
+      content::ClipboardEndpoint(ui::DataTransferEndpoint(GURL(test_url_1())),
+                                 base::BindLambdaForTesting([this]() {
+                                   return contents()->GetBrowserContext();
+                                 }),
+                                 *contents()->GetPrimaryMainFrame()),
+      /*metadata=*/
+      {.size = 1234, .format_type = ui::ClipboardFormatType::PlainTextType()},
+      MakeClipboardPasteData("text", "image", {}), future.GetCallback());
 
   // The dialog will stay up until a user action dismisses it, so `future`
   // shouldn't be ready yet.
@@ -720,6 +940,10 @@
 
   auto paste_data = future.Get();
   EXPECT_FALSE(paste_data);
+
+  if (machine_scope()) {
+    report_run_loop.Run();
+  }
 }
 #endif  // !BUILDFLAG(IS_CHROMEOS)
 
@@ -773,7 +997,8 @@
           return &set;
         }(),
         /*expected_trigger=*/"WEB_CONTENT_UPLOAD",
-        /*triggered_rules=*/{{0, {"8765", "report_rule_name"}}},
+        /*triggered_rules=*/
+        {{{0, machine_scope()}, {"8765", "report_rule_name"}}},
         /*expected_result=*/"EVENT_RESULT_ALLOWED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -821,10 +1046,10 @@
 #if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_P(DataControlsClipboardUtilsBrowserTest,
                        PasteReportedByDataControls_SourceRule) {
-  auto event_validator = event_report_validator_helper_->CreateValidator();
-  event_validator.ExpectNoReport();
-
-  data_controls::SetDataControls(browser()->profile()->GetPrefs(), {R"({
+  // By making a new profile for this test, we ensure we can prevent pasting to
+  // it by having the rule set in the source profile only.
+  Profile* source_profile = CreateAdditionalProfile();
+  data_controls::SetDataControls(source_profile->GetPrefs(), {R"({
                                    "name": "report_rule_name",
                                    "rule_id": "9753",
                                    "destinations": {
@@ -835,38 +1060,85 @@
                                    ]
                                  })"},
                                  machine_scope());
+
   data_controls::DesktopDataControlsDialogTestHelper helper(
       data_controls::DataControlsDialog::Type::kClipboardPasteBlock);
 
-  // By making a new profile for this test, we ensure we can prevent pasting to
-  // it by having the rule set in the source profile.
-  std::unique_ptr<Profile> destination_profile;
-  {
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    destination_profile = Profile::CreateProfile(
-        g_browser_process->profile_manager()->user_data_dir().Append(
-            FILE_PATH_LITERAL("DC Test Profile")),
-        /*delegate=*/nullptr, Profile::CreateMode::kSynchronous);
+  base::RunLoop report_run_loop;
+  auto event_validator = event_report_validator_helper_->CreateValidator();
+
+  // The event should only be reported when the policies are set at the machine
+  // level as we would otherwise be reporting from a different unmanaged or
+  // unaffiliated profile.
+  if (machine_scope()) {
+    event_validator.SetDoneClosure(report_run_loop.QuitClosure());
+    if (use_proto_format()) {
+      chrome::cros::reporting::proto::DlpSensitiveDataEvent expected_event;
+      expected_event.set_url(test_url_1());
+      expected_event.set_tab_url(test_url_1());
+      expected_event.set_source("OTHER_PROFILE");
+      expected_event.set_destination(test_url_1());
+      expected_event.set_content_type("text/plain");
+      expected_event.set_content_size(1234);
+      expected_event.set_trigger(
+          chrome::cros::reporting::proto::DataTransferEventTrigger::
+              WEB_CONTENT_UPLOAD);
+      expected_event.set_event_result(
+          chrome::cros::reporting::proto::EventResult::EVENT_RESULT_ALLOWED);
+
+      ::chrome::cros::reporting::proto::TriggeredRuleInfo triggered_rule;
+      triggered_rule.set_rule_id(9753);
+      triggered_rule.set_rule_name("report_rule_name");
+
+      *expected_event.add_triggered_rule_info() = triggered_rule;
+      expected_event.set_profile_identifier(
+          browser()->profile()->GetPath().AsUTF8Unsafe());
+      expected_event.set_profile_user_name(kUserName);
+
+      event_validator.ExpectSensitiveDataEvent(std::move(expected_event));
+    } else {
+      event_validator.ExpectDataControlsSensitiveDataEvent(
+          /*expected_url=*/test_url_1(),
+          /*expected_tab_url=*/test_url_1(),
+          /*expected_source=*/"OTHER_PROFILE",
+          /*expected_destination=*/test_url_1(),
+          /*expected_mimetypes=*/
+          []() {
+            static std::set<std::string> set = {"text/plain"};
+            return &set;
+          }(),
+          /*expected_trigger=*/"WEB_CONTENT_UPLOAD",
+          /*triggered_rules=*/
+          {{{0, machine_scope()}, {"9753", "report_rule_name"}}},
+          /*expected_result=*/"EVENT_RESULT_ALLOWED",
+          /*expected_profile_username=*/kUserName,
+          /*expected_profile_identifier=*/
+          browser()->profile()->GetPath().AsUTF8Unsafe(),
+          /*expected_content_size=*/1234);
+    }
+  } else {
+    event_validator.ExpectNoReport();
   }
 
   base::test::TestFuture<std::optional<content::ClipboardPasteData>> future;
   PasteIfAllowedByPolicy(
-      /*source=*/content::ClipboardEndpoint(
-          ui::DataTransferEndpoint(GURL("https://foo.com")),
-          base::BindLambdaForTesting(
-              [this]() { return contents()->GetBrowserContext(); }),
-          *contents()->GetPrimaryMainFrame()),
-      /*destination=*/
+      /*source=*/
       content::ClipboardEndpoint(
-          ui::DataTransferEndpoint(GURL("https://google.com")),
+          ui::DataTransferEndpoint(GURL(test_url_0())),
           base::BindLambdaForTesting(
-              [&destination_profile]() -> content::BrowserContext* {
-                return destination_profile.get();
+              [&source_profile]() -> content::BrowserContext* {
+                return source_profile;
               }),
           *contents()->GetPrimaryMainFrame()),
+      /*destination=*/
+      content::ClipboardEndpoint(ui::DataTransferEndpoint(GURL(test_url_1())),
+                                 base::BindLambdaForTesting([this]() {
+                                   return contents()->GetBrowserContext();
+                                 }),
+                                 *contents()->GetPrimaryMainFrame()),
       /*metadata=*/
-      {.size = 1234}, MakeClipboardPasteData("text", "image", {}),
-      future.GetCallback());
+      {.size = 1234, .format_type = ui::ClipboardFormatType::PlainTextType()},
+      MakeClipboardPasteData("text", "image", {}), future.GetCallback());
 
   ASSERT_FALSE(helper.dialog());
   auto paste_data = future.Get();
@@ -874,6 +1146,10 @@
   EXPECT_EQ(paste_data->text, u"text");
   EXPECT_EQ(std::string(paste_data->png.begin(), paste_data->png.end()),
             "image");
+
+  if (machine_scope()) {
+    report_run_loop.Run();
+  }
 }
 #endif  // !BUILDFLAG(IS_CHROMEOS)
 
@@ -951,7 +1227,7 @@
           return &set;
         }(),
         /*expected_trigger=*/"CLIPBOARD_COPY",
-        /*triggered_rules=*/{{0, {"1248", "report_only"}}},
+        /*triggered_rules=*/{{{0, machine_scope()}, {"1248", "report_only"}}},
         /*expected_result=*/"EVENT_RESULT_ALLOWED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -1042,7 +1318,7 @@
           return &set;
         }(),
         /*expected_trigger=*/"CLIPBOARD_COPY",
-        /*triggered_rules=*/{{0, {"987", "block"}}},
+        /*triggered_rules=*/{{{0, machine_scope()}, {"987", "block"}}},
         /*expected_result=*/"EVENT_RESULT_BLOCKED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -1140,7 +1416,7 @@
           return &set;
         }(),
         /*expected_trigger=*/"CLIPBOARD_COPY",
-        /*triggered_rules=*/{{0, {"3927", "warn"}}},
+        /*triggered_rules=*/{{{0, machine_scope()}, {"3927", "warn"}}},
         /*expected_result=*/"EVENT_RESULT_WARNED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -1243,7 +1519,7 @@
           return &set;
         }(),
         /*expected_trigger=*/"CLIPBOARD_COPY",
-        /*triggered_rules=*/{{0, {"101", "warn_cancel"}}},
+        /*triggered_rules=*/{{{0, machine_scope()}, {"101", "warn_cancel"}}},
         /*expected_result=*/"EVENT_RESULT_WARNED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -1349,7 +1625,7 @@
           return &set;
         }(),
         /*expected_trigger=*/"CLIPBOARD_COPY",
-        /*triggered_rules=*/{{0, {"12345", "warn_bypass"}}},
+        /*triggered_rules=*/{{{0, machine_scope()}, {"12345", "warn_bypass"}}},
         /*expected_result=*/"EVENT_RESULT_WARNED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -1441,7 +1717,7 @@
           return &set;
         }(),
         /*expected_trigger=*/"CLIPBOARD_COPY",
-        /*triggered_rules=*/{{0, {"12345", "warn_bypass"}}},
+        /*triggered_rules=*/{{{0, machine_scope()}, {"12345", "warn_bypass"}}},
         /*expected_result=*/"EVENT_RESULT_BYPASSED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -1511,7 +1787,7 @@
           return &set;
         }(),
         /*expected_trigger=*/"CLIPBOARD_COPY",
-        /*triggered_rules=*/{{0, {"111", "warn_bypass_os"}}},
+        /*triggered_rules=*/{{{0, machine_scope()}, {"111", "warn_bypass_os"}}},
         /*expected_result=*/"EVENT_RESULT_WARNED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -1606,7 +1882,7 @@
           return &set;
         }(),
         /*expected_trigger=*/"CLIPBOARD_COPY",
-        /*triggered_rules=*/{{0, {"111", "warn_bypass_os"}}},
+        /*triggered_rules=*/{{{0, machine_scope()}, {"111", "warn_bypass_os"}}},
         /*expected_result=*/"EVENT_RESULT_BYPASSED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -1721,7 +1997,8 @@
           return &set;
         }(),
         /*expected_trigger=*/"WEB_CONTENT_UPLOAD",
-        /*triggered_rules=*/{{0, {"131", "warn_on_all_pastes"}}},
+        /*triggered_rules=*/
+        {{{0, machine_scope()}, {"131", "warn_on_all_pastes"}}},
         /*expected_result=*/"EVENT_RESULT_WARNED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
@@ -1784,7 +2061,8 @@
           return &set;
         }(),
         /*expected_trigger=*/"WEB_CONTENT_UPLOAD",
-        /*triggered_rules=*/{{0, {"131", "warn_on_all_pastes"}}},
+        /*triggered_rules=*/
+        {{{0, machine_scope()}, {"131", "warn_on_all_pastes"}}},
         /*expected_result=*/"EVENT_RESULT_BYPASSED",
         /*expected_profile_username=*/kUserName,
         /*expected_profile_identifier=*/
diff --git a/chrome/browser/enterprise/idle/action.cc b/chrome/browser/enterprise/idle/action.cc
index 176780d..5890e88 100644
--- a/chrome/browser/enterprise/idle/action.cc
+++ b/chrome/browser/enterprise/idle/action.cc
@@ -10,6 +10,7 @@
 
 #include "base/callback_list.h"
 #include "base/check_is_test.h"
+#include "base/containers/contains.h"
 #include "base/containers/flat_map.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
diff --git a/chrome/browser/enterprise/idle/action.h b/chrome/browser/enterprise/idle/action.h
index eb55d34..0fe18b5 100644
--- a/chrome/browser/enterprise/idle/action.h
+++ b/chrome/browser/enterprise/idle/action.h
@@ -14,10 +14,13 @@
 #include "base/functional/callback.h"
 #include "base/no_destructor.h"
 #include "build/build_config.h"
-#include "chrome/browser/ui/idle_dialog.h"
 #include "components/enterprise/idle/action_type.h"
 #include "content/public/browser/browsing_data_remover.h"
 
+#if !BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/ui/idle_dialog.h"
+#endif  // !BUILDFLAG(IS_ANDROID)
+
 class Profile;
 
 namespace enterprise_idle {
@@ -86,8 +89,10 @@
       browsing_data_remover_for_testing_;
 };
 
+#if !BUILDFLAG(IS_ANDROID)
 IdleDialog::ActionSet ActionsToActionSet(
     const base::flat_set<ActionType>& action_types);
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 }  // namespace enterprise_idle
 
diff --git a/chrome/browser/enterprise/reporting/profile_report_generator_unittest.cc b/chrome/browser/enterprise/reporting/profile_report_generator_unittest.cc
index ad4440c..104e674 100644
--- a/chrome/browser/enterprise/reporting/profile_report_generator_unittest.cc
+++ b/chrome/browser/enterprise/reporting/profile_report_generator_unittest.cc
@@ -264,7 +264,7 @@
             report->chrome_signed_in_user().obfuscated_gaia_id());
 }
 
-TEST_F(ProfileReportGeneratorTest, ProfileIdObfuscate) {
+TEST_F(ProfileReportGeneratorTest, ProfileIdObfuscateByDefault) {
   base::test::TestFuture<std::unique_ptr<em::ChromeUserProfileInfo>>
       test_future;
   generator_.MaybeGenerate(profile()->GetPath(), ReportType::kProfileReport,
@@ -297,6 +297,46 @@
   EXPECT_NE(report->id(), report3->id());
 }
 
+#if !BUILDFLAG(IS_CHROMEOS)
+
+TEST_F(ProfileReportGeneratorTest, ProfileIdNotObfuscatedInAffiliatedProfile) {
+  profile()->GetProfilePolicyConnector()->SetUserAffiliationIdsForTesting(
+      {kAffiliationId1});
+  g_browser_process->browser_policy_connector()
+      ->SetDeviceAffiliatedIdsForTesting({kAffiliationId1});
+
+  base::test::TestFuture<std::unique_ptr<em::ChromeUserProfileInfo>>
+      test_future;
+  generator_.MaybeGenerate(profile()->GetPath(), ReportType::kProfileReport,
+                           test_future.GetCallback());
+
+  auto report = test_future.Take();
+  ASSERT_TRUE(report);
+  EXPECT_EQ(GetProfileName(), report->name());
+  EXPECT_EQ(profile()->GetPath().AsUTF8Unsafe(), report->id());
+  EXPECT_TRUE(report->is_detail_available());
+}
+
+TEST_F(ProfileReportGeneratorTest, ProfileIdObfuscatedInUnaffiliatedProfile) {
+  profile()->GetProfilePolicyConnector()->SetUserAffiliationIdsForTesting(
+      {kAffiliationId1});
+  g_browser_process->browser_policy_connector()
+      ->SetDeviceAffiliatedIdsForTesting({kAffiliationId2});
+
+  base::test::TestFuture<std::unique_ptr<em::ChromeUserProfileInfo>>
+      test_future;
+  generator_.MaybeGenerate(profile()->GetPath(), ReportType::kProfileReport,
+                           test_future.GetCallback());
+
+  auto report = test_future.Take();
+  ASSERT_TRUE(report);
+  EXPECT_EQ(GetProfileName(), report->name());
+  EXPECT_NE(profile()->GetPath().AsUTF8Unsafe(), report->id());
+  EXPECT_TRUE(report->is_detail_available());
+}
+
+#endif  // !BUILDFLAG(IS_CHROMEOS)
+
 TEST_F(ProfileReportGeneratorTest, PoliciesDisabled) {
   // Users' profile info is collected by default.
   std::unique_ptr<em::ChromeUserProfileInfo> report = GenerateReport();
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index a8e054b..6f1709a 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -1132,6 +1132,7 @@
       ]
 
       deps += [
+        "//chrome/browser/certificate_provider",
         "//chrome/browser/chromeos/extensions/desk_api",
         "//chrome/browser/chromeos/extensions/external_loader",
         "//chrome/browser/chromeos/extensions/login_screen/login_state",
diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
index d12e2110..ac3ead3 100644
--- a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
+++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
@@ -27,6 +27,55 @@
 namespace extensions {
 namespace {
 
+class ManagementSetEnabledFunctionInstallPromptDelegate
+    : public InstallPromptDelegate {
+ public:
+  ManagementSetEnabledFunctionInstallPromptDelegate(
+      content::WebContents* web_contents,
+      content::BrowserContext* browser_context,
+      const Extension* extension,
+      base::OnceCallback<void(bool)> callback)
+      : install_prompt_(new ExtensionInstallPrompt(web_contents)),
+        callback_(std::move(callback)) {
+    ExtensionInstallPrompt::PromptType type =
+        ExtensionInstallPrompt::GetReEnablePromptTypeForExtension(
+            browser_context, extension);
+    install_prompt_->ShowDialog(
+        base::BindOnce(&ManagementSetEnabledFunctionInstallPromptDelegate::
+                           OnInstallPromptDone,
+                       weak_factory_.GetWeakPtr()),
+        extension, nullptr,
+        std::make_unique<ExtensionInstallPrompt::Prompt>(type),
+        ExtensionInstallPrompt::GetDefaultShowDialogCallback());
+  }
+
+  ManagementSetEnabledFunctionInstallPromptDelegate(
+      const ManagementSetEnabledFunctionInstallPromptDelegate&) = delete;
+  ManagementSetEnabledFunctionInstallPromptDelegate& operator=(
+      const ManagementSetEnabledFunctionInstallPromptDelegate&) = delete;
+
+  ~ManagementSetEnabledFunctionInstallPromptDelegate() override = default;
+
+ private:
+  void OnInstallPromptDone(
+      ExtensionInstallPrompt::DoneCallbackPayload payload) {
+    // This dialog doesn't support the "withhold permissions" checkbox.
+    DCHECK_NE(
+        payload.result,
+        ExtensionInstallPrompt::Result::ACCEPTED_WITH_WITHHELD_PERMISSIONS);
+    std::move(callback_).Run(payload.result ==
+                             ExtensionInstallPrompt::Result::ACCEPTED);
+  }
+
+  // Used for prompting to re-enable items with permissions escalation updates.
+  std::unique_ptr<ExtensionInstallPrompt> install_prompt_;
+
+  base::OnceCallback<void(bool)> callback_;
+
+  base::WeakPtrFactory<ManagementSetEnabledFunctionInstallPromptDelegate>
+      weak_factory_{this};
+};
+
 class ManagementUninstallFunctionUninstallDialogDelegate
     : public ExtensionUninstallDialog::Delegate,
       public UninstallDialogDelegate {
@@ -114,6 +163,16 @@
   return ::extensions::GetLaunchType(prefs, extension);
 }
 
+std::unique_ptr<InstallPromptDelegate>
+ChromeManagementAPIDelegate::SetEnabledFunctionDelegate(
+    content::WebContents* web_contents,
+    content::BrowserContext* browser_context,
+    const Extension* extension,
+    base::OnceCallback<void(bool)> callback) const {
+  return std::make_unique<ManagementSetEnabledFunctionInstallPromptDelegate>(
+      web_contents, browser_context, extension, std::move(callback));
+}
+
 std::unique_ptr<UninstallDialogDelegate>
 ChromeManagementAPIDelegate::UninstallFunctionDelegate(
     ManagementUninstallFunctionBase* function,
diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate_android.cc b/chrome/browser/extensions/api/management/chrome_management_api_delegate_android.cc
index 6b71ef3e..1d82f7c 100644
--- a/chrome/browser/extensions/api/management/chrome_management_api_delegate_android.cc
+++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate_android.cc
@@ -16,20 +16,6 @@
   NOTREACHED();
 }
 
-std::unique_ptr<InstallPromptDelegate>
-ChromeManagementAPIDelegate::SetEnabledFunctionDelegate(
-    content::WebContents* web_contents,
-    content::BrowserContext* browser_context,
-    const Extension* extension,
-    base::OnceCallback<void(bool)> callback) const {
-  // TODO(crbug.com/410932770): Show a permission dialog. For now, pretend that
-  // the user accepted it. When this dialog is built, also enable the test
-  // ManagementApiUnitTest.SetEnabled_IncreasedPermissions.
-  NOTIMPLEMENTED() << "Skipping enable extension dialog";
-  std::move(callback).Run(true);
-  return nullptr;
-}
-
 bool ChromeManagementAPIDelegate::CreateAppShortcutFunctionDelegate(
     ManagementCreateAppShortcutFunction* function,
     const Extension* extension,
diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate_nonandroid.cc b/chrome/browser/extensions/api/management/chrome_management_api_delegate_nonandroid.cc
index 09ec6c3..e7e3602c 100644
--- a/chrome/browser/extensions/api/management/chrome_management_api_delegate_nonandroid.cc
+++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate_nonandroid.cc
@@ -77,55 +77,6 @@
     ManagementAPIDelegate::InstallOrLaunchWebAppResult;
 using InstallableCheckResult = web_app::InstallableCheckResult;
 
-class ManagementSetEnabledFunctionInstallPromptDelegate
-    : public InstallPromptDelegate {
- public:
-  ManagementSetEnabledFunctionInstallPromptDelegate(
-      content::WebContents* web_contents,
-      content::BrowserContext* browser_context,
-      const Extension* extension,
-      base::OnceCallback<void(bool)> callback)
-      : install_prompt_(new ExtensionInstallPrompt(web_contents)),
-        callback_(std::move(callback)) {
-    ExtensionInstallPrompt::PromptType type =
-        ExtensionInstallPrompt::GetReEnablePromptTypeForExtension(
-            browser_context, extension);
-    install_prompt_->ShowDialog(
-        base::BindOnce(&ManagementSetEnabledFunctionInstallPromptDelegate::
-                           OnInstallPromptDone,
-                       weak_factory_.GetWeakPtr()),
-        extension, nullptr,
-        std::make_unique<ExtensionInstallPrompt::Prompt>(type),
-        ExtensionInstallPrompt::GetDefaultShowDialogCallback());
-  }
-
-  ManagementSetEnabledFunctionInstallPromptDelegate(
-      const ManagementSetEnabledFunctionInstallPromptDelegate&) = delete;
-  ManagementSetEnabledFunctionInstallPromptDelegate& operator=(
-      const ManagementSetEnabledFunctionInstallPromptDelegate&) = delete;
-
-  ~ManagementSetEnabledFunctionInstallPromptDelegate() override = default;
-
- private:
-  void OnInstallPromptDone(
-      ExtensionInstallPrompt::DoneCallbackPayload payload) {
-    // This dialog doesn't support the "withhold permissions" checkbox.
-    DCHECK_NE(
-        payload.result,
-        ExtensionInstallPrompt::Result::ACCEPTED_WITH_WITHHELD_PERMISSIONS);
-    std::move(callback_).Run(payload.result ==
-                             ExtensionInstallPrompt::Result::ACCEPTED);
-  }
-
-  // Used for prompting to re-enable items with permissions escalation updates.
-  std::unique_ptr<ExtensionInstallPrompt> install_prompt_;
-
-  base::OnceCallback<void(bool)> callback_;
-
-  base::WeakPtrFactory<ManagementSetEnabledFunctionInstallPromptDelegate>
-      weak_factory_{this};
-};
-
 void OnGenerateAppForLinkCompleted(
     ManagementGenerateAppForLinkFunction* function,
     const webapps::AppId& app_id,
@@ -345,16 +296,6 @@
   return true;
 }
 
-std::unique_ptr<InstallPromptDelegate>
-ChromeManagementAPIDelegate::SetEnabledFunctionDelegate(
-    content::WebContents* web_contents,
-    content::BrowserContext* browser_context,
-    const Extension* extension,
-    base::OnceCallback<void(bool)> callback) const {
-  return std::make_unique<ManagementSetEnabledFunctionInstallPromptDelegate>(
-      web_contents, browser_context, extension, std::move(callback));
-}
-
 bool ChromeManagementAPIDelegate::CreateAppShortcutFunctionDelegate(
     ManagementCreateAppShortcutFunction* function,
     const Extension* extension,
diff --git a/chrome/browser/extensions/api/management/management_api_unittest.cc b/chrome/browser/extensions/api/management/management_api_unittest.cc
index 5471ec63..42b1b20a 100644
--- a/chrome/browser/extensions/api/management/management_api_unittest.cc
+++ b/chrome/browser/extensions/api/management/management_api_unittest.cc
@@ -802,9 +802,6 @@
   // Due to a permission increase, prefs will contain escalation information.
   EXPECT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id));
 
-// TODO(crbug.com/410932770): Port the rest of this test to desktop Android
-// when the extension permission dialog is supported.
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   // 1) Confirm re-enable prompt without user gesture, expect the extension to
   // stay disabled.
   {
@@ -866,7 +863,6 @@
   known_perms = prefs->GetGrantedPermissions(extension_id);
   ASSERT_TRUE(known_perms);
   EXPECT_FALSE(known_perms->IsEmpty());
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 }
 
 TEST_F(ManagementApiUnitTest,
diff --git a/chrome/browser/extensions/api/notifications/extension_notification_handler.cc b/chrome/browser/extensions/api/notifications/extension_notification_handler.cc
index b8a573ab..83da735 100644
--- a/chrome/browser/extensions/api/notifications/extension_notification_handler.cc
+++ b/chrome/browser/extensions/api/notifications/extension_notification_handler.cc
@@ -108,7 +108,8 @@
 void ExtensionNotificationHandler::DisableNotifications(
     Profile* profile,
     const GURL& origin,
-    const std::optional<std::string>& notification_id) {
+    const std::optional<std::string>& notification_id,
+    const std::optional<bool>& is_suspicious) {
   message_center::NotifierId notifier_id(
       message_center::NotifierType::APPLICATION, origin.GetHost());
   NotifierStateTrackerFactory::GetForProfile(profile)->SetNotifierEnabled(
diff --git a/chrome/browser/extensions/api/notifications/extension_notification_handler.h b/chrome/browser/extensions/api/notifications/extension_notification_handler.h
index 69e51ea..54d29798 100644
--- a/chrome/browser/extensions/api/notifications/extension_notification_handler.h
+++ b/chrome/browser/extensions/api/notifications/extension_notification_handler.h
@@ -42,10 +42,10 @@
                const std::optional<int>& action_index,
                const std::optional<std::u16string>& reply,
                base::OnceClosure completed_closure) override;
-  void DisableNotifications(
-      Profile* profile,
-      const GURL& origin,
-      const std::optional<std::string>& notification_id) override;
+  void DisableNotifications(Profile* profile,
+                            const GURL& origin,
+                            const std::optional<std::string>& notification_id,
+                            const std::optional<bool>& is_suspicious) override;
   void OpenSettings(Profile* profile, const GURL& origin) override;
 
  protected:
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index 51d3552..34721c5 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/extensions/extension_browser_test_util.h"
 #include "chrome/browser/extensions/extension_install_prompt.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/extensions/load_error_reporter.h"
 #include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/extensions/window_controller.h"
@@ -945,24 +946,13 @@
 
 void ExtensionBrowserTest::CloseTabForWebContents(
     content::WebContents* web_contents) {
-#if BUILDFLAG(IS_ANDROID)
-  TabModel* tab_model = TabModelList::GetTabModelForWebContents(web_contents);
-  CHECK(tab_model);
-  for (int index = 0; index < tab_model->GetTabCount(); ++index) {
-    if (tab_model->GetWebContentsAt(index) == web_contents) {
-      tab_model->CloseTabAt(index);
-      return;
-    }
-  }
-  NOTREACHED() << "WebContents not found";
-#else
-  Browser* browser = chrome::FindBrowserWithTab(web_contents);
-  CHECK(browser);
-  int index = browser->tab_strip_model()->GetIndexOfWebContents(web_contents);
-  CHECK_GE(index, 0) << "WebContents not found";
-  return browser->tab_strip_model()->CloseWebContentsAt(
-      index, TabCloseTypes::CLOSE_NONE);
-#endif
+  content::WebContentsDestroyedWatcher destroyed_watcher(web_contents);
+  TabListInterface* tab_list = nullptr;
+  int tab_index = -1;
+  ASSERT_TRUE(ExtensionTabUtil::GetTabListInterface(*web_contents, &tab_list,
+                                                    &tab_index));
+  tab_list->CloseTab(tab_list->GetTab(tab_index)->GetHandle());
+  destroyed_watcher.Wait();
 }
 
 base::Value ExtensionBrowserTest::ExecuteScriptInBackgroundPage(
diff --git a/chrome/browser/extensions/extension_view_host.cc b/chrome/browser/extensions/extension_view_host.cc
index e95263c..6241c7d 100644
--- a/chrome/browser/extensions/extension_view_host.cc
+++ b/chrome/browser/extensions/extension_view_host.cc
@@ -14,6 +14,7 @@
 #include "extensions/browser/process_util.h"
 #include "extensions/buildflags/buildflags.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
+#include "ui/events/keycodes/keyboard_codes.h"
 
 static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
 
diff --git a/chrome/browser/extensions/service_worker_apitest.cc b/chrome/browser/extensions/service_worker_apitest.cc
index e4ea116..47b38a2 100644
--- a/chrome/browser/extensions/service_worker_apitest.cc
+++ b/chrome/browser/extensions/service_worker_apitest.cc
@@ -2208,6 +2208,47 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBasedBackgroundTest,
+                       UnloadSplitModeExtensionStopsWorkers) {
+  Browser* browser_incognito =
+      OpenURLOffTheRecord(profile(), GURL("about:blank"));
+  ASSERT_TRUE(browser_incognito);
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(R"({
+    "name": "Incognito Test Extension",
+    "version": "0.1",
+    "manifest_version": 3,
+    "background": {"service_worker": "worker.js"},
+    "incognito": "split"
+  })");
+
+  test_dir.WriteFile(FILE_PATH_LITERAL("worker.js"),
+                     R"(// Intentionally left blank.)");
+
+  const Extension* extension =
+      LoadExtension(test_dir.UnpackedPath(), {.allow_in_incognito = true});
+  ASSERT_TRUE(extension);
+
+  std::vector<WorkerId> regular_workers =
+      ProcessManager::Get(profile())->GetAllWorkersIdsForTesting();
+  content::BrowserContext* incognito_context = browser_incognito->profile();
+  std::vector<WorkerId> incognito_workers =
+      ProcessManager::Get(incognito_context)->GetAllWorkersIdsForTesting();
+  EXPECT_EQ(regular_workers.size(), 1ul);
+  EXPECT_EQ(incognito_workers.size(), 1ul);
+
+  // Ensure unloading the extension stops both workers.
+  UnloadExtension(extension->id());
+
+  regular_workers =
+      ProcessManager::Get(profile())->GetAllWorkersIdsForTesting();
+  incognito_workers =
+      ProcessManager::Get(incognito_context)->GetAllWorkersIdsForTesting();
+  EXPECT_EQ(regular_workers.size(), 0ul);
+  EXPECT_EQ(incognito_workers.size(), 0ul);
+}
+
 // Test extension with OnInstalled listener can be successfully updated when,
 // 1) Was allowed in incognito.
 // 2) An incognito window was open.
diff --git a/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.cc b/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.cc
index b26f4f96..7611243 100644
--- a/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.cc
+++ b/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.cc
@@ -26,7 +26,6 @@
 #include "components/autofill/core/browser/data_model/payments/ewallet.h"
 #include "components/facilitated_payments/android/device_delegate_android.h"
 #include "components/facilitated_payments/core/browser/facilitated_payments_app_info_list.h"
-#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.h"
 #include "components/facilitated_payments/core/browser/network_api/multiple_request_facilitated_payments_network_interface.h"
 #include "components/facilitated_payments/core/browser/payment_link_manager.h"
 #include "components/facilitated_payments/core/browser/pix_account_linking_manager.h"
@@ -77,23 +76,6 @@
   return pdm ? &pdm->payments_data_manager() : nullptr;
 }
 
-payments::facilitated::FacilitatedPaymentsNetworkInterface*
-ChromeFacilitatedPaymentsClient::GetFacilitatedPaymentsNetworkInterface() {
-  if (!facilitated_payments_network_interface_) {
-    Profile* profile =
-        Profile::FromBrowserContext(GetWebContents().GetBrowserContext());
-    if (!profile) {
-      return nullptr;
-    }
-    facilitated_payments_network_interface_ = std::make_unique<
-        payments::facilitated::FacilitatedPaymentsNetworkInterface>(
-        profile->GetURLLoaderFactory(),
-        IdentityManagerFactory::GetForProfile(profile->GetOriginalProfile()),
-        GetPaymentsDataManager(), profile->IsOffTheRecord());
-  }
-  return facilitated_payments_network_interface_.get();
-}
-
 payments::facilitated::MultipleRequestFacilitatedPaymentsNetworkInterface*
 ChromeFacilitatedPaymentsClient::
     GetMultipleRequestFacilitatedPaymentsNetworkInterface() {
diff --git a/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.h b/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.h
index c06d9b6..c807501 100644
--- a/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.h
+++ b/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client.h
@@ -79,8 +79,6 @@
   // This returns nullptr if the `Profile` associated is null.
   autofill::PaymentsDataManager* GetPaymentsDataManager() final;
   // This returns nullptr if the `Profile` associated is null.
-  payments::facilitated::FacilitatedPaymentsNetworkInterface*
-  GetFacilitatedPaymentsNetworkInterface() final;
   payments::facilitated::MultipleRequestFacilitatedPaymentsNetworkInterface*
   GetMultipleRequestFacilitatedPaymentsNetworkInterface() final;
   // This returns std::nullopt if the `Profile` associated is null.
@@ -123,8 +121,6 @@
   payments::facilitated::ContentFacilitatedPaymentsDriverFactory
       driver_factory_;
 
-  std::unique_ptr<payments::facilitated::FacilitatedPaymentsNetworkInterface>
-      facilitated_payments_network_interface_;
   std::unique_ptr<
       payments::facilitated::MultipleRequestFacilitatedPaymentsNetworkInterface>
       multiple_request_facilitated_payments_network_interface_;
diff --git a/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client_unittest.cc b/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client_unittest.cc
index 9c5c1dc..7e5bcf3 100644
--- a/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client_unittest.cc
+++ b/chrome/browser/facilitated_payments/ui/chrome_facilitated_payments_client_unittest.cc
@@ -104,8 +104,10 @@
 }
 
 TEST_F(ChromeFacilitatedPaymentsClientTest,
-       GetFacilitatedPaymentsNetworkInterface) {
-  EXPECT_NE(nullptr, base_client().GetFacilitatedPaymentsNetworkInterface());
+       GetMultipleRequestFacilitatedPaymentsNetworkInterface) {
+  EXPECT_NE(
+      nullptr,
+      base_client().GetMultipleRequestFacilitatedPaymentsNetworkInterface());
 }
 
 // Test the client forwards call to show Pix FOP selector to the controller.
diff --git a/chrome/browser/fingerprinting_protection/canvas_interventions_browsertest.cc b/chrome/browser/fingerprinting_protection/canvas_interventions_browsertest.cc
index 0c29256..e13d87ba 100644
--- a/chrome/browser/fingerprinting_protection/canvas_interventions_browsertest.cc
+++ b/chrome/browser/fingerprinting_protection/canvas_interventions_browsertest.cc
@@ -23,6 +23,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -228,6 +229,21 @@
     return ParseTokenFromJsResult(js_result);
   }
 
+  std::optional<blink::NoiseToken> GetRendererTokenFromSharedWorker(
+      const content::ToRenderFrameHost& to_rfh) {
+    constexpr const char kScript[] = R"(
+  new Promise(resolve => {
+    worker.port.addEventListener('message', (event) => {
+      resolve(event.data);
+    }, { once: true });
+
+    worker.port.postMessage('get-canvas-noise-token');
+  });
+  )";
+    auto js_result = content::EvalJs(to_rfh, kScript);
+    return ParseTokenFromJsResult(js_result);
+  }
+
   std::optional<blink::NoiseToken> GetRendererTokenFromWorker(
       const content::ToRenderFrameHost& to_rfh) {
     constexpr const char kScript[] = R"(
@@ -239,7 +255,6 @@
     worker.postMessage('get-canvas-noise-token');
   });
   )";
-
     auto js_result = content::EvalJs(to_rfh, kScript);
     return ParseTokenFromJsResult(js_result);
   }
@@ -672,6 +687,113 @@
   tracking_protection_settings->AddTrackingProtectionException(url);
 }
 
+IN_PROC_BROWSER_TEST_P(CanvasInterventionsBrowserTest, SharedWorkerSameToken) {
+  GURL url = embedded_https_test_server().GetURL(
+      "a.com",
+      "/workers/create_shared_worker.html?worker_url=/"
+      "fingerprinting_protection/canvas_noise_token_shared_worker.js");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+  if (should_browsing_mode_have_token()) {
+    EXPECT_NE(GetRendererTokenFromSharedWorker(web_contents()), std::nullopt);
+    // TODO(https://crbug.com/442616874): change to EXPECT_EQ once we key canvas
+    // noise tokens with StorageKey.
+    EXPECT_NE(GetRendererTokenFromSharedWorker(web_contents()),
+              GetBrowserTokenFromPage(web_contents()));
+  } else {
+    EXPECT_EQ(GetRendererTokenFromSharedWorker(web_contents()), std::nullopt);
+  }
+}
+
+IN_PROC_BROWSER_TEST_P(CanvasInterventionsBrowserTest,
+                       SharedWorkerDifferentTabSameToken) {
+  GURL url = embedded_https_test_server().GetURL(
+      "a.com",
+      "/workers/create_shared_worker.html?worker_url=/"
+      "fingerprinting_protection/canvas_noise_token_shared_worker.js");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+  content::WebContents* other_tab = chrome::AddSelectedTabWithURL(
+      GetBrowser(), url, ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
+  EXPECT_TRUE(content::WaitForLoadStop(other_tab));
+
+  auto first_tab_token = GetRendererTokenFromSharedWorker(web_contents());
+  auto second_tab_token = GetRendererTokenFromSharedWorker(other_tab);
+
+  if (should_browsing_mode_have_token()) {
+    EXPECT_NE(first_tab_token, std::nullopt);
+    // TODO(https://crbug.com/442616874): change to EXPECT_EQ once we key canvas
+    // noise tokens with StorageKey.
+    EXPECT_NE(first_tab_token, GetBrowserTokenFromPage(web_contents()));
+  } else {
+    EXPECT_EQ(first_tab_token, std::nullopt);
+  }
+
+  EXPECT_EQ(first_tab_token, second_tab_token);
+}
+
+IN_PROC_BROWSER_TEST_P(CanvasInterventionsBrowserTest,
+                       SharedWorkerRegularAndIncognitoDifferentToken) {
+  if (GetParam().browser_mode == BrowserMode::kIncognito) {
+    GTEST_SKIP() << "This test tests both profiles";
+  }
+
+  GURL url = embedded_https_test_server().GetURL(
+      "a.com",
+      "/workers/create_shared_worker.html?worker_url=/"
+      "fingerprinting_protection/canvas_noise_token_shared_worker.js");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+  auto browser_token = GetRendererTokenFromSharedWorker(web_contents());
+
+  content::WebContents* incognito_contents =
+      CreateIncognitoBrowser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(content::NavigateToURL(incognito_contents, url));
+
+  auto incognito_browser_token =
+      GetRendererTokenFromSharedWorker(incognito_contents);
+
+  if (GetParam().feature_state == FeatureState::kDisabled) {
+    EXPECT_EQ(browser_token, incognito_browser_token);
+  } else {
+    EXPECT_NE(browser_token, incognito_browser_token);
+  }
+}
+
+IN_PROC_BROWSER_TEST_P(CanvasInterventionsBrowserTest,
+                       SharedWorkerSubframeSameToken) {
+  GURL main_frame_url = embedded_https_test_server().GetURL(
+      "a.com",
+      "/workers/create_shared_worker.html?worker_url=/"
+      "fingerprinting_protection/canvas_noise_token_shared_worker.js");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), main_frame_url));
+  auto main_frame_shw_token = GetRendererTokenFromSharedWorker(web_contents());
+
+  CreateChildFrame();
+  auto* iframe = content::ChildFrameAt(web_contents(), 0);
+  ASSERT_TRUE(iframe);
+
+  GURL iframe_url = embedded_https_test_server().GetURL(
+      "b.com",
+      "/workers/create_shared_worker.html?worker_url=/"
+      "fingerprinting_protection/canvas_noise_token_shared_worker.js");
+  ASSERT_TRUE(content::NavigateToURLFromRenderer(iframe, iframe_url));
+  iframe = content::ChildFrameAt(web_contents(), 0);
+  ASSERT_TRUE(iframe);
+  auto iframe_shw_token = GetRendererTokenFromSharedWorker(iframe);
+
+  if (should_browsing_mode_have_token()) {
+    EXPECT_NE(iframe_shw_token, std::nullopt);
+    // TODO(https://crbug.com/442616874): change to EXPECT_EQ once we key canvas
+    // noise tokens with StorageKey.
+    EXPECT_NE(iframe_shw_token, GetBrowserTokenFromPage(web_contents()));
+  } else {
+    EXPECT_EQ(iframe_shw_token, std::nullopt);
+  }
+
+  EXPECT_EQ(iframe_shw_token, main_frame_shw_token);
+}
+
 IN_PROC_BROWSER_TEST_P(CanvasInterventionsBrowserTest,
                        DedicatedWorkerSameToken) {
   const GURL url = embedded_https_test_server().GetURL(
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index c18c7e7..69417ce 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -119,6 +119,11 @@
     "expiry_milestone": 145
   },
   {
+    "name":"aim-prototype-tab-picker",
+    "owners": ["ameurhosni@google.com","alionadangla@google.com","radunitescu@google.com"],
+    "expiry_milestone": 145
+  },
+  {
     "name": "aim-server-eligibility",
     "owners": ["ameurhosni@google.com", "mahmadi@chromium.org"],
     "expiry_milestone": 145
@@ -276,7 +281,7 @@
   {
     "name": "android-data-importer-service",
     "owners": [ "treib@chromium.org", "fsenra@google.com" ],
-    "expiry_milestone": 143
+    "expiry_milestone": 150
   },
   {
     "name": "android-desktop-density",
@@ -1079,7 +1084,7 @@
   {
     "name": "autofill-sync-ewallet-accounts",
     "owners": ["junhuihe@chromium.org", "chrome-payments-eng@google.com", "payments-autofill-team@google.com"],
-    "expiry_milestone": 140
+    "expiry_milestone": 151
   },
   {
     "name": "autofill-third-party-mode-content-provider",
@@ -1904,11 +1909,6 @@
   "expiry_milestone": 145
   },
   {
-    "name": "content-notification-experiment",
-    "owners": [ "tinazwang@google.com", "chrome-sherlock@google.com"],
-    "expiry_milestone": 135
-  },
-  {
     "name": "content-notification-provisional-ignore-conditions",
     "owners": [ "tinazwang@google.com", "chrome-sherlock@google.com"],
     "expiry_milestone": 135
@@ -1919,11 +1919,6 @@
     "expiry_milestone": 144
   },
   {
-    "name": "content-settings-partitioning",
-    "owners": [ "brettw@chromium.org", "lt-web-apps-team@google.com"],
-    "expiry_milestone": 132
-  },
-  {
     "name": "contextual-cueing",
     "owners": [ "perrier@chromium.org","jeffreycohen@chromium.org"],
     "expiry_milestone": 160
@@ -4869,7 +4864,7 @@
   {
     "name": "ewallet-payments",
     "owners": [ "junhuihe@google.com", "chrome-payments-eng@google.com", "payments-autofill-team@google.com" ],
-    "expiry_milestone": 140
+    "expiry_milestone": 151
   },
   {
     "name": "exclude-display-in-mirror-mode",
@@ -6256,6 +6251,11 @@
     "expiry_milestone": 147
   },
   {
+    "name": "ios-tips-notifications-string-alternatives",
+    "owners": [ "gayane@google.com", "bling-mony-pod@google.com" ],
+    "expiry_milestone": 146
+  },
+  {
     "name": "ios-trusted-vault-notification",
     "owners": [ "ylahodiuk@google.com", "rgod@google.com" ],
     "expiry_milestone": 143
@@ -7980,7 +7980,7 @@
   {
     "name": "payment-link-detection",
     "owners": [ "junhuihe@google.com", "chrome-payments-eng@google.com", "payments-autofill-team@google.com" ],
-    "expiry_milestone": 140
+    "expiry_milestone": 151
   },
   {
     "name": "pcct-minimum-height",
@@ -9148,11 +9148,6 @@
     "expiry_milestone": 137
   },
   {
-    "name": "separate-profiles-for-managed-accounts",
-    "owners": [ "treib@chromium.org", "chrome-signin-team@google.com" ],
-    "expiry_milestone": 143
-  },
-  {
     "name": "separate-web-app-shortcut-badge-icon",
     "owners": ["tbarzic@chromium.org", "owenzhang@google.com"],
     "expiry_milestone": 130
@@ -9335,6 +9330,11 @@
     "expiry_milestone": 140
   },
   {
+    "name": "show-tab-grid-on-start",
+    "owners": [ "achara@google.com", "ewannpv@google.com", "bling-flags@google.com" ],
+    "expiry_milestone": 146
+  },
+  {
     "name": "show-tab-groups-mac-system-menu",
     "owners": ["top-chrome-desktop-ui@google.com"],
     "expiry_milestone": 150
@@ -9558,11 +9558,6 @@
     "expiry_milestone": 150
   },
   {
-    "name": "support-multiple-server-requests-for-pix-payments",
-    "owners": [ "siyua@chromium.org", "payments-autofill-team@google.com" ],
-    "expiry_milestone": 140
-  },
-  {
     "name": "support-tool-screenshot",
     "owners": [ "//chrome/browser/support_tool/OWNERS" ],
     "expiry_milestone": 140
@@ -9762,11 +9757,6 @@
     "expiry_milestone": 149
   },
   {
-    "name": "tab-strip-transition-in-desktop-window",
-    "owners": [ "aishwaryarj@google.com", "clank-large-form-factors@google.com" ],
-    "expiry_milestone": 143
-  },
-  {
     "name": "tab-switcher-color-blend-animate",
     "owners": [ "skym@chromium.org", "mfiaz@google.com" ],
     "expiry_milestone": 140
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 56d7891..2801ab3 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -326,10 +326,6 @@
     "The container-type property was recently changed to not add layout "
     "containment, this allows users to temporarily disable this change.";
 
-const char kContentSettingsPartitioningName[] = "Content Settings Partitioning";
-const char kContentSettingsPartitioningDescription[] =
-    "Partition content settings by StoragePartitions";
-
 #if BUILDFLAG(IS_ANDROID)
 const char kCredentialManagementThirdPartyWebApiRequestForwardingName[] =
     "Credential Management Third Party Web API Request Forwarding";
@@ -988,14 +984,6 @@
     "When enabled, the cached server credit card data from autofill will be "
     "pushed into the shared storage database for the payments origin.";
 
-#if BUILDFLAG(IS_ANDROID)
-const char kAutofillSyncEwalletAccountsName[] =
-    "Sync eWallet accounts from Google Payments";
-const char kAutofillSyncEwalletAccountsDescription[] =
-    "When enabled, eWallet accounts are synced from the Google Payments "
-    "servers and displayed on the payment methods settings page.";
-#endif  // BUILDFLAG(IS_ANDROID)
-
 const char kAutofillUnmaskCardRequestTimeoutName[] =
     "Timeout for the credit card unmask request";
 const char kAutofillUnmaskCardRequestTimeoutDescription[] =
@@ -4139,13 +4127,6 @@
     "This option configures TLS Trust Anchor IDs, allowing compatible servers "
     "to select between available certificates issued by different CAs.";
 
-#if !BUILDFLAG(IS_ANDROID)
-const char kPinnedTabToastOnCloseName[] = "Pinned Tab Toast On Close";
-const char kPinnedTabToastOnCloseDescription[] =
-    "Enable to show a confirmation toast that displays when a pinned tab is "
-    "closed via the keyboard shortcut.";
-#endif
-
 #if BUILDFLAG(IS_ANDROID)
 const char kTopControlsRefactorName[] = "Top Controls Refactor";
 const char kTopControlsRefactorDescription[] =
@@ -5124,11 +5105,6 @@
     "Enables the enterprise data controls on Android for restricting copy and "
     "paste actions for the clipboard.";
 
-const char kEwalletPaymentsName[] = "Enable eWallet payments";
-const char kEwalletPaymentsDescription[] =
-    "When enabled, Chrome will offer to pay with eWallet accounts if a payment "
-    "link is detected.";
-
 const char kExternalNavigationDebugLogsName[] =
     "External Navigation Debug Logs";
 const char kExternalNavigationDebugLogsDescription[] =
@@ -5287,10 +5263,6 @@
     "Enables storing successful query/match in the omnibox shortcut database "
     "on Android";
 
-const char kPaymentLinkDetectionName[] = "Enable payment link detection";
-const char kPaymentLinkDetectionDescription[] =
-    "Enables payment link detection in the DOM.";
-
 const char kPCCTMinimumHeightName[] =
     "Change the minimum height of pCCT to 30%.";
 const char kPCCTMinimumHeightDescription[] =
@@ -5464,12 +5436,6 @@
     "Enabling #allow-non-family-link-url-filter-mode is also required for "
     "users who do not sign-in.";
 
-const char kSupportMultipleServerRequestsForPixPaymentsName[] =
-    "Support multiple server requests for Pix payments";
-const char kSupportMultipleServerRequestsForPixPaymentsDescription[] =
-    "When enabled, the network interface with Google Payments supports "
-    "handling multiple concurrent requests for Pix flows.";
-
 const char kHistoryPaneAndroidName[] = "History Pane Android";
 const char kHistoryPaneAndroidDescription[] =
     "Enables showing a new pane in the hub that displays History.";
@@ -5503,12 +5469,6 @@
 const char kTabStripMouseCloseResizeDelayDescription[] =
     "Delays resizing the tab strip when closing a tab with the mouse.";
 
-const char kTabStripTransitionInDesktopWindowName[] =
-    "Tab Strip Transition in Desktop Window";
-const char kTabStripTransitionInDesktopWindowDescription[] =
-    "Allows hiding / showing the tab strip with varying desktop window widths "
-    "by initiating a fade transition.";
-
 const char kToolbarSnapshotRefactorName[] = "Toolbar Snapshot Refactor";
 const char kToolbarSnapshotRefactorDescription[] =
     "Updates the margin and snapshotting of the Toolbar on Android.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 8dc7e43..7dfcdd6 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -234,9 +234,6 @@
 extern const char kContainerTypeNoLayoutContainmentName[];
 extern const char kContainerTypeNoLayoutContainmentDescription[];
 
-extern const char kContentSettingsPartitioningName[];
-extern const char kContentSettingsPartitioningDescription[];
-
 #if BUILDFLAG(IS_ANDROID)
 extern const char kCredentialManagementThirdPartyWebApiRequestForwardingName[];
 extern const char
@@ -583,11 +580,6 @@
 extern const char kAutofillMoreProminentPopupName[];
 extern const char kAutofillMoreProminentPopupDescription[];
 
-#if BUILDFLAG(IS_ANDROID)
-extern const char kAutofillSyncEwalletAccountsName[];
-extern const char kAutofillSyncEwalletAccountsDescription[];
-#endif  // BUILDFLAG(IS_ANDROID)
-
 extern const char kAutofillPaymentsFieldSwappingName[];
 extern const char kAutofillPaymentsFieldSwappingDescription[];
 
@@ -2386,11 +2378,6 @@
 extern const char kTLSTrustAnchorIDsName[];
 extern const char kTLSTrustAnchorIDsDescription[];
 
-#if !BUILDFLAG(IS_ANDROID)
-extern const char kPinnedTabToastOnCloseName[];
-extern const char kPinnedTabToastOnCloseDescription[];
-#endif
-
 #if BUILDFLAG(IS_ANDROID)
 extern const char kTopControlsRefactorName[];
 extern const char kTopControlsRefactorDescription[];
@@ -2961,9 +2948,6 @@
 extern const char kEnableClientCertificateProvisioningOnAndroidName[];
 extern const char kEnableClientCertificateProvisioningOnAndroidDescription[];
 
-extern const char kEwalletPaymentsName[];
-extern const char kEwalletPaymentsDescription[];
-
 extern const char kExternalNavigationDebugLogsName[];
 extern const char kExternalNavigationDebugLogsDescription[];
 
@@ -3068,9 +3052,6 @@
 extern const char kOmniboxShortcutsAndroidName[];
 extern const char kOmniboxShortcutsAndroidDescription[];
 
-extern const char kPaymentLinkDetectionName[];
-extern const char kPaymentLinkDetectionDescription[];
-
 extern const char kPCCTMinimumHeightName[];
 extern const char kPCCTMinimumHeightDescription[];
 
@@ -3164,9 +3145,6 @@
 extern const char kSupervisedUserInterstitialWithoutApprovalsName[];
 extern const char kSupervisedUserInterstitialWithoutApprovalsDescription[];
 
-extern const char kSupportMultipleServerRequestsForPixPaymentsName[];
-extern const char kSupportMultipleServerRequestsForPixPaymentsDescription[];
-
 extern const char kHistoryPaneAndroidName[];
 extern const char kHistoryPaneAndroidDescription[];
 
@@ -3197,9 +3175,6 @@
 extern const char kTabStripMouseCloseResizeDelayName[];
 extern const char kTabStripMouseCloseResizeDelayDescription[];
 
-extern const char kTabStripTransitionInDesktopWindowName[];
-extern const char kTabStripTransitionInDesktopWindowDescription[];
-
 extern const char kToolbarSnapshotRefactorName[];
 extern const char kToolbarSnapshotRefactorDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 3d3e2b0b..3d6b898 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -359,7 +359,6 @@
     &kMostVisitedTilesReselect,
     &kMultiInstanceApplicationStatusCleanup,
     &kMvcUpdateViewWhenModelChanged,
-    &kNativePageTransitionHardwareCapture,
     &kNavBarColorAnimation,
     &kNotificationPermissionVariant,
     &kNotificationPermissionBottomSheet,
@@ -429,7 +428,6 @@
     &kTabStripIncognitoMigration,
     &kTabStripLayoutOptimization,
     &kTabStripMouseCloseResizeDelay,
-    &kTabStripTransitionInDesktopWindow,
     &kTabSwitcherDragDropAndroid,
     &kTabSwitcherForeignFaviconSupport,
     &kTabSwitcherGroupSuggestionsAndroid,
@@ -927,9 +925,6 @@
 
 BASE_FEATURE(kMvcUpdateViewWhenModelChanged, base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kNativePageTransitionHardwareCapture,
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kNavBarColorAnimation, base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kNotificationPermissionVariant, base::FEATURE_DISABLED_BY_DEFAULT);
@@ -1074,9 +1069,6 @@
 
 BASE_FEATURE(kTabStripMouseCloseResizeDelay, base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kTabStripTransitionInDesktopWindow,
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kTabSwitcherDragDropAndroid, base::FEATURE_DISABLED_BY_DEFAULT);
 
 BASE_FEATURE(kTabSwitcherForeignFaviconSupport,
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 1604538..be3cd0c 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -190,7 +190,6 @@
 BASE_DECLARE_FEATURE(kMostVisitedTilesReselect);
 BASE_DECLARE_FEATURE(kMultiInstanceApplicationStatusCleanup);
 BASE_DECLARE_FEATURE(kMvcUpdateViewWhenModelChanged);
-BASE_DECLARE_FEATURE(kNativePageTransitionHardwareCapture);
 BASE_DECLARE_FEATURE(kNavBarColorAnimation);
 BASE_DECLARE_FEATURE(kNotificationPermissionVariant);
 BASE_DECLARE_FEATURE(kNotificationPermissionBottomSheet);
@@ -262,7 +261,6 @@
 BASE_DECLARE_FEATURE(kTabStripIncognitoMigration);
 BASE_DECLARE_FEATURE(kTabStripLayoutOptimization);
 BASE_DECLARE_FEATURE(kTabStripMouseCloseResizeDelay);
-BASE_DECLARE_FEATURE(kTabStripTransitionInDesktopWindow);
 BASE_DECLARE_FEATURE(kTabSwitcherDragDropAndroid);
 BASE_DECLARE_FEATURE(kTabSwitcherForeignFaviconSupport);
 BASE_DECLARE_FEATURE(kTabSwitcherGroupSuggestionsAndroid);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 91df58b..bf2e030 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -489,8 +489,6 @@
     public static final String MULTI_INSTANCE_APPLICATION_STATUS_CLEANUP =
             "MultiInstanceApplicationStatusCleanup";
     public static final String MVC_UPDATE_VIEW_WHEN_MODEL_CHANGED = "MvcUpdateViewWhenModelChanged";
-    public static final String NATIVE_PAGE_TRANSITION_HARDWARE_CAPTURE =
-            "NativePageTransitionHardwareCapture";
     public static final String NAV_BAR_COLOR_ANIMATION = "NavBarColorAnimation";
     public static final String NEW_TAB_PAGE_CUSTOMIZATION = "NewTabPageCustomization";
     public static final String NEW_TAB_PAGE_CUSTOMIZATION_FOR_MVT = "NewTabPageCustomizationForMvt";
@@ -662,8 +660,6 @@
     public static final String TAB_STRIP_INCOGNITO_MIGRATION = "TabStripIncognitoMigration";
     public static final String TAB_STRIP_LAYOUT_OPTIMIZATION = "TabStripLayoutOptimization";
     public static final String TAB_STRIP_MOUSE_CLOSE_RESIZE_DELAY = "TabStripMouseCloseResizeDelay";
-    public static final String TAB_STRIP_TRANSITION_IN_DESKTOP_WINDOW =
-            "TabStripTransitionInDesktopWindow";
     public static final String TAB_SWITCHER_DRAG_DROP_ANDROID = "TabSwitcherDragDropAndroid";
     public static final String TAB_SWITCHER_FOREIGN_FAVICON_SUPPORT =
             "TabSwitcherForeignFaviconSupport";
diff --git a/chrome/browser/gesturenav/android/java/src/org/chromium/chrome/browser/gesturenav/NativePageBitmapCapturer.java b/chrome/browser/gesturenav/android/java/src/org/chromium/chrome/browser/gesturenav/NativePageBitmapCapturer.java
index eaa0467..5908415 100644
--- a/chrome/browser/gesturenav/android/java/src/org/chromium/chrome/browser/gesturenav/NativePageBitmapCapturer.java
+++ b/chrome/browser/gesturenav/android/java/src/org/chromium/chrome/browser/gesturenav/NativePageBitmapCapturer.java
@@ -210,9 +210,7 @@
     }
 
     private static boolean enableHardwareDraw() {
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
-                && ChromeFeatureList.isEnabled(
-                        ChromeFeatureList.NATIVE_PAGE_TRANSITION_HARDWARE_CAPTURE);
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
     }
 
     private static boolean enableAsyncNativePageScreenshot() {
diff --git a/chrome/browser/glic/BUILD.gn b/chrome/browser/glic/BUILD.gn
index 2e8e12f..dfb8993f 100644
--- a/chrome/browser/glic/BUILD.gn
+++ b/chrome/browser/glic/BUILD.gn
@@ -121,6 +121,8 @@
     "host/auth_controller.cc",
     "host/context/glic_active_browser_sharing_manager.cc",
     "host/context/glic_active_browser_sharing_manager.h",
+    "host/context/glic_active_pinned_focused_tab_manager.cc",
+    "host/context/glic_active_pinned_focused_tab_manager.h",
     "host/context/glic_delegating_sharing_manager.cc",
     "host/context/glic_delegating_sharing_manager.h",
     "host/context/glic_empty_focused_browser_manager.cc",
diff --git a/chrome/browser/glic/host/context/glic_active_pinned_focused_tab_manager.cc b/chrome/browser/glic/host/context/glic_active_pinned_focused_tab_manager.cc
new file mode 100644
index 0000000..c6724aea
--- /dev/null
+++ b/chrome/browser/glic/host/context/glic_active_pinned_focused_tab_manager.cc
@@ -0,0 +1,101 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/glic/host/context/glic_active_pinned_focused_tab_manager.h"
+
+#include "chrome/browser/glic/host/context/glic_sharing_utils.h"
+#include "chrome/browser/glic/public/context/glic_sharing_manager.h"
+
+namespace glic {
+
+GlicActivePinnedFocusedTabManager::GlicActivePinnedFocusedTabManager(
+    Profile* profile,
+    GlicSharingManager* sharing_manager)
+    : sharing_manager_(sharing_manager),
+      active_tab_tracker_(profile),
+      profile_(profile) {}
+
+GlicActivePinnedFocusedTabManager::~GlicActivePinnedFocusedTabManager() =
+    default;
+
+base::CallbackListSubscription
+GlicActivePinnedFocusedTabManager::AddFocusedTabChangedCallback(
+    FocusedTabChangedCallback callback) {
+  // Lazy-initialize upstream subscriptions here.
+  if (!active_tab_changed_subscription_) {
+    active_tab_changed_subscription_ =
+        active_tab_tracker_.AddActiveTabChangedCallback(base::BindRepeating(
+            &GlicActivePinnedFocusedTabManager::OnActiveTabChanged,
+            base::Unretained(this)));
+  }
+  if (!tab_pinning_status_changed_subscription_ && sharing_manager_) {
+    tab_pinning_status_changed_subscription_ =
+        sharing_manager_->AddTabPinningStatusChangedCallback(
+            base::BindRepeating(
+                &GlicActivePinnedFocusedTabManager::OnTabPinningStatusChanged,
+                base::Unretained(this)));
+  }
+
+  return focused_tab_changed_callback_list_.Add(std::move(callback));
+}
+
+base::CallbackListSubscription
+GlicActivePinnedFocusedTabManager::AddFocusedTabDataChangedCallback(
+    FocusedTabDataChangedCallback callback) {
+  // TODO(b:444463509): Implement focused tab data changed tracking.
+  return focused_tab_data_changed_callback_list_.Add(std::move(callback));
+}
+
+bool GlicActivePinnedFocusedTabManager::IsTabFocused(
+    tabs::TabHandle tab_handle) const {
+  tabs::TabInterface* active_tab = active_tab_tracker_.GetActiveTab();
+  if (active_tab && tab_handle == active_tab->GetHandle() &&
+      sharing_manager_->IsTabPinned(tab_handle) &&
+      IsTabValidForSharing(tab_handle.Get()->GetContents())) {
+    return true;
+  }
+
+  return false;
+}
+
+FocusedTabData GlicActivePinnedFocusedTabManager::GetFocusedTabData() {
+  tabs::TabInterface* active_tab = active_tab_tracker_.GetActiveTab();
+
+  if (!active_tab) {
+    return FocusedTabData(std::string("no focusable tab"), nullptr);
+  }
+
+  if (!sharing_manager_->IsTabPinned(active_tab->GetHandle()) ||
+      !IsTabValidForSharing(active_tab->GetContents())) {
+    return FocusedTabData(std::string("no focusable tab"), active_tab);
+  }
+
+  return FocusedTabData(active_tab);
+}
+
+void GlicActivePinnedFocusedTabManager::OnActiveTabChanged(
+    tabs::TabInterface* active_tab) {
+  UpdateFocusedTab();
+}
+
+void GlicActivePinnedFocusedTabManager::OnTabPinningStatusChanged(
+    tabs::TabInterface* tab,
+    bool status) {
+  tabs::TabInterface* active_tab = active_tab_tracker_.GetActiveTab();
+
+  if (active_tab && tab && active_tab == tab) {
+    UpdateFocusedTab();
+  }
+}
+
+void GlicActivePinnedFocusedTabManager::UpdateFocusedTab() {
+  NotifyFocusedTabChanged(GetFocusedTabData());
+}
+
+void GlicActivePinnedFocusedTabManager::NotifyFocusedTabChanged(
+    const FocusedTabData& focused_tab) {
+  focused_tab_changed_callback_list_.Notify(focused_tab);
+}
+
+}  // namespace glic
diff --git a/chrome/browser/glic/host/context/glic_active_pinned_focused_tab_manager.h b/chrome/browser/glic/host/context/glic_active_pinned_focused_tab_manager.h
new file mode 100644
index 0000000..c5e8ebd
--- /dev/null
+++ b/chrome/browser/glic/host/context/glic_active_pinned_focused_tab_manager.h
@@ -0,0 +1,89 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_GLIC_HOST_CONTEXT_GLIC_ACTIVE_PINNED_FOCUSED_TAB_MANAGER_H_
+#define CHROME_BROWSER_GLIC_HOST_CONTEXT_GLIC_ACTIVE_PINNED_FOCUSED_TAB_MANAGER_H_
+
+#include "chrome/browser/glic/host/context/glic_focused_tab_manager_interface.h"
+#include "chrome/browser/glic/host/context/glic_sharing_utils.h"
+
+namespace glic {
+
+class GlicSharingManager;
+
+// Focused tab manager that combines "active" and "pinned" status. The
+// active tab in the active browser is considered focused
+// if it is both valid (see `GlicSharingUtils`) AND pinned for sharing.
+//
+// If both are not true, then it is considered the focused candidate.
+//
+// If the last active browser is not valid (see `GlicSharingUtils`), or there
+// are no browsers, then there is neither a focused tab nor focused tab
+// candidate.
+//
+// Note: makes no guarantees about de-duping of events, so subscribers should
+// should handle de-duping in cases where that matters.
+class GlicActivePinnedFocusedTabManager
+    : public GlicFocusedTabManagerInterface {
+ public:
+  explicit GlicActivePinnedFocusedTabManager(
+      Profile* profile,
+      GlicSharingManager* sharing_manager);
+  ~GlicActivePinnedFocusedTabManager() override;
+  GlicActivePinnedFocusedTabManager(const GlicActivePinnedFocusedTabManager&) =
+      delete;
+  GlicActivePinnedFocusedTabManager& operator=(
+      const GlicActivePinnedFocusedTabManager&) = delete;
+
+  // GlicFocusedTabManagerInterface implementation.
+  using FocusedTabChangedCallback =
+      base::RepeatingCallback<void(const FocusedTabData&)>;
+  base::CallbackListSubscription AddFocusedTabChangedCallback(
+      FocusedTabChangedCallback callback) override;
+  using FocusedTabDataChangedCallback =
+      base::RepeatingCallback<void(const glic::mojom::TabData*)>;
+  base::CallbackListSubscription AddFocusedTabDataChangedCallback(
+      FocusedTabDataChangedCallback callback) override;
+  bool IsTabFocused(tabs::TabHandle tab_handle) const override;
+  FocusedTabData GetFocusedTabData() override;
+
+ private:
+  // Callback for active tab changes.
+  void OnActiveTabChanged(tabs::TabInterface* active_tab);
+
+  // Callback for tab pinning status changes.
+  void OnTabPinningStatusChanged(tabs::TabInterface* tab, bool status);
+
+  // Updates the currently focused tab and notifies subscribers when changed.
+  void UpdateFocusedTab();
+
+  // Notifies subscribers of a change to the focused tab.
+  void NotifyFocusedTabChanged(const FocusedTabData& focused_tab);
+
+  // Source of truth for pinned tabs.
+  raw_ptr<GlicSharingManager> sharing_manager_;
+
+  // TODO(b:444463509): refactor into a shared singleton for the profile.
+  GlicActiveTabForProfileTracker active_tab_tracker_;
+
+  // Callback for changes to the active tab.
+  base::CallbackListSubscription active_tab_changed_subscription_;
+
+  // Callback for changes to tab pinning status.
+  base::CallbackListSubscription tab_pinning_status_changed_subscription_;
+
+  // List of callbacks to fire when the focused tab changes.
+  base::RepeatingCallbackList<void(const FocusedTabData&)>
+      focused_tab_changed_callback_list_;
+
+  // List of callbacks to fire when the focused tab data changes.
+  base::RepeatingCallbackList<void(const glic::mojom::TabData*)>
+      focused_tab_data_changed_callback_list_;
+
+  raw_ptr<Profile> profile_;
+};
+
+}  // namespace glic
+
+#endif  // CHROME_BROWSER_GLIC_HOST_CONTEXT_GLIC_ACTIVE_PINNED_FOCUSED_TAB_MANAGER_H_
diff --git a/chrome/browser/glic/host/context/glic_sharing_utils.cc b/chrome/browser/glic/host/context/glic_sharing_utils.cc
index 4b602667..ea39dda9 100644
--- a/chrome/browser/glic/host/context/glic_sharing_utils.cc
+++ b/chrome/browser/glic/host/context/glic_sharing_utils.cc
@@ -101,7 +101,7 @@
   return active_tab_changed_callback_list_.Add(std::move(callback));
 }
 
-tabs::TabInterface* GlicActiveTabForProfileTracker::GetActiveTab() {
+tabs::TabInterface* GlicActiveTabForProfileTracker::GetActiveTab() const {
   return last_notified_tab_.get();
 }
 
diff --git a/chrome/browser/glic/host/context/glic_sharing_utils.h b/chrome/browser/glic/host/context/glic_sharing_utils.h
index dc422b8..22225df9 100644
--- a/chrome/browser/glic/host/context/glic_sharing_utils.h
+++ b/chrome/browser/glic/host/context/glic_sharing_utils.h
@@ -45,7 +45,7 @@
       base::RepeatingCallback<void(tabs::TabInterface* tab)> callback);
 
   // Get the last notified active tab.
-  tabs::TabInterface* GetActiveTab();
+  tabs::TabInterface* GetActiveTab() const;
 
  private:
   // BrowserListObserver.
diff --git a/chrome/browser/glic/host/glic_actor_click_tool_interactive_uitest.cc b/chrome/browser/glic/host/glic_actor_click_tool_interactive_uitest.cc
index 7cd0c9f..c54273d 100644
--- a/chrome/browser/glic/host/glic_actor_click_tool_interactive_uitest.cc
+++ b/chrome/browser/glic/host/glic_actor_click_tool_interactive_uitest.cc
@@ -60,6 +60,22 @@
                   WaitForJsResult(kNewActorTabId, "expect_single_left_click"));
 }
 
+// A click on a button in a web component should work, but a click on another
+// element in the component should not.
+IN_PROC_BROWSER_TEST_F(GlicActorUiTest, ClickActionInWebComponent) {
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kNewActorTabId);
+  const GURL task_url = embedded_test_server()->GetURL(
+      "/actor/page_with_web_component_button.html");
+
+  RunTestSequence(
+      InitializeWithOpenGlicWindow(),
+      StartActorTaskInNewTab(task_url, kNewActorTabId),
+      GetPageContextFromFocusedTab(),
+      ClickAction(kClickableButtonLabel, ClickAction::LEFT,
+                  ClickAction::SINGLE),
+      WaitForJsResult(kNewActorTabId, "() => document.title", "Clicked"));
+}
+
 IN_PROC_BROWSER_TEST_F(GlicActorUiTest, DblClickActionSucceeds) {
   DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kNewActorTabId);
 
diff --git a/chrome/browser/glic/host/glic_actor_general_interactive_uitest.cc b/chrome/browser/glic/host/glic_actor_general_interactive_uitest.cc
index cd488ebf..ce4daa1 100644
--- a/chrome/browser/glic/host/glic_actor_general_interactive_uitest.cc
+++ b/chrome/browser/glic/host/glic_actor_general_interactive_uitest.cc
@@ -74,13 +74,7 @@
   return WaitAction(task_id_, std::move(expected_result));
 }
 
-// TODO(crbug.com/448882109): Disable failing test on Mac.
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_CreateTaskAndNavigate DISABLED_CreateTaskAndNavigate
-#else
-#define MAYBE_CreateTaskAndNavigate CreateTaskAndNavigate
-#endif
-IN_PROC_BROWSER_TEST_F(GlicActorGeneralUiTest, MAYBE_CreateTaskAndNavigate) {
+IN_PROC_BROWSER_TEST_F(GlicActorGeneralUiTest, CreateTaskAndNavigate) {
   DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kNewActorTabId);
 
   base::HistogramTester histogram_tester;
@@ -92,12 +86,8 @@
                   WaitForWebContentsReady(kNewActorTabId, task_url));
 
   // Two samples of 1 tab for CreateTab, Navigate actions.
-  // The durations should not be zero.
   histogram_tester.ExpectUniqueSample("Actor.PageContext.TabCount", 1, 2);
-  histogram_tester.ExpectBucketCount("Actor.PageContext.APC.Duration", 0, 0);
   histogram_tester.ExpectTotalCount("Actor.PageContext.APC.Duration", 2);
-  histogram_tester.ExpectBucketCount("Actor.PageContext.Screenshot.Duration", 0,
-                                     0);
   histogram_tester.ExpectTotalCount("Actor.PageContext.Screenshot.Duration", 2);
 }
 
diff --git a/chrome/browser/glic/host/glic_api_browsertest.cc b/chrome/browser/glic/host/glic_api_browsertest.cc
index 447ba6a..b3cf0017 100644
--- a/chrome/browser/glic/host/glic_api_browsertest.cc
+++ b/chrome/browser/glic/host/glic_api_browsertest.cc
@@ -538,7 +538,6 @@
 #define MAYBE_testReload testReload
 #endif
 IN_PROC_BROWSER_TEST_P(GlicApiTest, MAYBE_testReload) {
-  TODO_SKIP_BROKEN_MULTI_INSTANCE_TEST();
   RunTestSequence(
       OpenGlicWindow(GlicWindowMode::kDetached, GlicInstrumentMode::kNone));
   WebUIStateListener listener(GetHost());
@@ -554,14 +553,13 @@
 }
 
 IN_PROC_BROWSER_TEST_P(GlicApiTest, testReloadWebUi) {
-  TODO_SKIP_BROKEN_MULTI_INSTANCE_TEST();
-  WebUIStateListener listener(GetHost());
   RunTestSequence(
       OpenGlicWindow(GlicWindowMode::kDetached, GlicInstrumentMode::kNone));
+  WebUIStateListener listener(GetHost());
   ExecuteJsTest();
 
   listener.WaitForWebUiState(mojom::WebUiState::kReady);
-  window_controller().Reload();
+  ReloadGlicWebui();
   listener.WaitForWebUiState(mojom::WebUiState::kUninitialized);
   ExecuteJsTest();
 
@@ -1741,7 +1739,7 @@
                                  .Set("isFirstRun", true))});
 
   WebUIStateListener listener(GetHost());
-  window_controller().Reload();
+  ReloadGlicWebui();
   listener.WaitForWebUiState(mojom::WebUiState::kUninitialized);
 
   ExecuteJsTest(
@@ -2206,6 +2204,28 @@
   ContinueJsTest();
 }
 
+// TODO(https://crbug.com/449764057): Flakes/fails on all by windows.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_testGetPageMetadataOnNavigation testGetPageMetadataOnNavigation
+#else
+#define MAYBE_testGetPageMetadataOnNavigation \
+  DISABLED_testGetPageMetadataOnNavigation
+#endif
+IN_PROC_BROWSER_TEST_P(GlicApiTestWithOneTab,
+                       MAYBE_testGetPageMetadataOnNavigation) {
+  TODO_SKIP_BROKEN_MULTI_INSTANCE_TEST();
+  // Runs the JS test until the first `advanceToNextStep()`.
+  ExecuteJsTest();
+
+  // The JS test is now paused. We can now navigate the tab.
+  RunTestSequence(NavigateWebContents(
+      kFirstTab,
+      InProcessBrowserTest::embedded_test_server()->GetURL("/title1.html")));
+
+  // Continue the JS test to verify the metadata update.
+  ContinueJsTest();
+}
+
 IN_PROC_BROWSER_TEST_P(GlicApiTestWithOneTab, testGetPageMetadataTabDestroyed) {
   TODO_SKIP_BROKEN_MULTI_INSTANCE_TEST();
   // Runs the JS test until the first `advanceToNextStep()`.
diff --git a/chrome/browser/glic/host/host.cc b/chrome/browser/glic/host/host.cc
index d214e7b..7de06dc 100644
--- a/chrome/browser/glic/host/host.cc
+++ b/chrome/browser/glic/host/host.cc
@@ -76,6 +76,15 @@
   contents_.reset();
 }
 
+void Host::Reload() {
+  auto* contents = webui_contents();
+  if (!contents) {
+    return;
+  }
+  contents->GetController().Reload(content::ReloadType::BYPASSING_CACHE,
+                                   /*check_for_repost=*/false);
+}
+
 void Host::CreateContents(bool initially_hidden) {
   if (!contents_) {
     contents_ = std::make_unique<WebUIContentsContainer>(
diff --git a/chrome/browser/glic/host/host.h b/chrome/browser/glic/host/host.h
index 80910c3..2f23532 100644
--- a/chrome/browser/glic/host/host.h
+++ b/chrome/browser/glic/host/host.h
@@ -178,6 +178,9 @@
   // Delete the owned web contents and prepare for destruction.
   void Shutdown();
 
+  // Reload the web contents, if it is present.
+  void Reload();
+
   // Creates the web contents that will own the Glic WebUI.
   // `initially_hidden` value is only relevant when
   // `kGlicGuestContentsVisibilityState` flag is enabled, otherwise the default
@@ -294,6 +297,8 @@
   // Returns the current panel state.
   const mojom::PanelState& GetPanelState(GlicWebClientAccess* client) const;
 
+  base::WeakPtr<Host> GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); }
+
  private:
   friend class HostManager;
 
@@ -357,6 +362,7 @@
 
   // The current view in the primary page handler.
   mojom::CurrentView primary_current_view_ = mojom::CurrentView::kConversation;
+  base::WeakPtrFactory<Host> weak_ptr_factory_{this};
 };
 
 // A Host::Delegate which does nothing. For chrome://glic tabs or inactive
diff --git a/chrome/browser/glic/service/glic_instance_impl.cc b/chrome/browser/glic/service/glic_instance_impl.cc
index 6aa0677..e0af9e75 100644
--- a/chrome/browser/glic/service/glic_instance_impl.cc
+++ b/chrome/browser/glic/service/glic_instance_impl.cc
@@ -13,8 +13,8 @@
 #include "chrome/browser/contextual_cueing/contextual_cueing_service_factory.h"
 #include "chrome/browser/glic/fre/glic_fre_controller.h"
 #include "chrome/browser/glic/glic_zero_state_suggestions_manager.h"
+#include "chrome/browser/glic/host/context/glic_active_pinned_focused_tab_manager.h"
 #include "chrome/browser/glic/host/context/glic_empty_focused_browser_manager.h"
-#include "chrome/browser/glic/host/context/glic_empty_focused_tab_manager.h"
 #include "chrome/browser/glic/host/context/glic_screenshot_capturer.h"
 #include "chrome/browser/glic/host/context/glic_sharing_manager_impl.h"
 #include "chrome/browser/glic/host/host.h"
@@ -110,7 +110,9 @@
       id_(instance_id),
       host_(profile_, this, this),
       sharing_manager_(
-          std::make_unique<GlicEmptyFocusedTabManager>(),
+          std::make_unique<GlicActivePinnedFocusedTabManager>(
+              profile,
+              &sharing_manager_),
           std::make_unique<GlicEmptyFocusedBrowserManager>(),
           std::make_unique<GlicPinnedTabManager>(profile, this, metrics),
           profile,
@@ -461,8 +463,8 @@
 void GlicInstanceImpl::ShowInactiveSidePanelEmbedderFor(
     tabs::TabInterface* tab) {
   auto& entry = BindTab(tab);
-  entry.embedder =
-      GlicInactiveSidePanelUi::CreateForBackgroundTab(tab->GetWeakPtr(), *this);
+  entry.embedder = GlicInactiveSidePanelUi::CreateForBackgroundTab(
+      tab->GetWeakPtr(), host().webui_contents(), *this);
 }
 
 void GlicInstanceImpl::SetActiveEmbedderAndNotifyStateChange(
diff --git a/chrome/browser/glic/test_support/glic_api_test.cc b/chrome/browser/glic/test_support/glic_api_test.cc
index 05889b7..5c2b3a1 100644
--- a/chrome/browser/glic/test_support/glic_api_test.cc
+++ b/chrome/browser/glic/test_support/glic_api_test.cc
@@ -6,12 +6,17 @@
 
 namespace glic {
 
-WebUIStateListener::WebUIStateListener(Host* host) : host_(host) {
+WebUIStateListener::WebUIStateListener(Host* host)
+    : host_(host ? host->GetWeakPtr() : nullptr) {
+  CHECK(host_);
   host_->AddObserver(this);
   states_.push_back(host_->GetPrimaryWebUiState());
 }
 
 WebUIStateListener::~WebUIStateListener() {
+  if (!host_) {
+    return;
+  }
   host_->RemoveObserver(this);
 }
 
@@ -22,6 +27,7 @@
 // Returns if `state` has been seen. Consumes all observed states up to the
 // point where this state is seen.
 void WebUIStateListener::WaitForWebUiState(mojom::WebUiState state) {
+  ASSERT_TRUE(host_);
   ASSERT_TRUE(base::test::RunUntil([&]() {
     while (!states_.empty()) {
       if (states_.front() != state) {
diff --git a/chrome/browser/glic/test_support/glic_api_test.h b/chrome/browser/glic/test_support/glic_api_test.h
index bc6ab53e..a7286de0 100644
--- a/chrome/browser/glic/test_support/glic_api_test.h
+++ b/chrome/browser/glic/test_support/glic_api_test.h
@@ -105,7 +105,7 @@
   void WaitForWebUiState(mojom::WebUiState state);
 
  private:
-  raw_ptr<Host> host_;
+  base::WeakPtr<Host> host_;
   std::deque<mojom::WebUiState> states_;
 };
 
@@ -188,10 +188,8 @@
   }
 
   Host* GetHost() {
-    Profile* profile = T::browser()->profile();
-    return &GlicKeyedServiceFactory::GetGlicKeyedService(profile)
-                ->GetInstanceForActiveTab(T::browser())
-                ->host();
+    GlicInstance* instance = T::GetGlicInstance();
+    return instance ? &instance->host() : nullptr;
   }
 
   // Run the test typescript function. The typescript function must have the
diff --git a/chrome/browser/glic/test_support/interactive_glic_test.h b/chrome/browser/glic/test_support/interactive_glic_test.h
index 832ad60..5062e64 100644
--- a/chrome/browser/glic/test_support/interactive_glic_test.h
+++ b/chrome/browser/glic/test_support/interactive_glic_test.h
@@ -533,6 +533,12 @@
     actor_service->GetActorUiStateManager()->OnUiEvent(start_task_event);
   }
 
+  void ReloadGlicWebui() {
+    Host* host = GetHost();
+    CHECK(host);
+    host->Reload();
+  }
+
  protected:
   GlicKeyedService* glic_service() {
     return GlicKeyedServiceFactory::GetGlicKeyedService(
diff --git a/chrome/browser/glic/test_support/non_interactive_glic_test.cc b/chrome/browser/glic/test_support/non_interactive_glic_test.cc
index 235de36..c41ddcd 100644
--- a/chrome/browser/glic/test_support/non_interactive_glic_test.cc
+++ b/chrome/browser/glic/test_support/non_interactive_glic_test.cc
@@ -8,7 +8,9 @@
 
 namespace glic {
 
-NonInteractiveGlicTest::NonInteractiveGlicTest() = default;
+NonInteractiveGlicTest::NonInteractiveGlicTest() {
+  GlicFocusedBrowserManager::SetTestingModeForTesting(true);
+}
 
 NonInteractiveGlicTest::NonInteractiveGlicTest(
     const base::FieldTrialParams& glic_params,
diff --git a/chrome/browser/glic/widget/glic_floating_ui.cc b/chrome/browser/glic/widget/glic_floating_ui.cc
index 91581e99..e5fa0a58 100644
--- a/chrome/browser/glic/widget/glic_floating_ui.cc
+++ b/chrome/browser/glic/widget/glic_floating_ui.cc
@@ -121,6 +121,7 @@
 
 void GlicFloatingUi::Close() {
   glic_widget_.reset();
+  delegate_->WillCloseFor(/*tab=*/nullptr);
 }
 
 void GlicFloatingUi::ClosePanel() {
diff --git a/chrome/browser/glic/widget/glic_inactive_side_panel_ui.cc b/chrome/browser/glic/widget/glic_inactive_side_panel_ui.cc
index 777d661..75856b7 100644
--- a/chrome/browser/glic/widget/glic_inactive_side_panel_ui.cc
+++ b/chrome/browser/glic/widget/glic_inactive_side_panel_ui.cc
@@ -41,12 +41,15 @@
 std::unique_ptr<GlicInactiveSidePanelUi>
 GlicInactiveSidePanelUi::CreateForBackgroundTab(
     base::WeakPtr<tabs::TabInterface> tab,
+    content::WebContents* glic_webui_contents,
     GlicUiEmbedder::Delegate& delegate) {
   // Using `new` to access a private constructor.
   auto inactive_side_panel =
       base::WrapUnique(new GlicInactiveSidePanelUi(tab, delegate));
   // Mark the side panel for showing next time the tab becomes active.
   inactive_side_panel->Show();
+  inactive_side_panel->inactive_view_controller_.CaptureScreenshot(
+      glic_webui_contents);
   return inactive_side_panel;
 }
 
diff --git a/chrome/browser/glic/widget/glic_inactive_side_panel_ui.h b/chrome/browser/glic/widget/glic_inactive_side_panel_ui.h
index 061f245..f651d0d 100644
--- a/chrome/browser/glic/widget/glic_inactive_side_panel_ui.h
+++ b/chrome/browser/glic/widget/glic_inactive_side_panel_ui.h
@@ -34,6 +34,7 @@
       GlicUiEmbedder::Delegate& delegate);
   static std::unique_ptr<GlicInactiveSidePanelUi> CreateForBackgroundTab(
       base::WeakPtr<tabs::TabInterface> tab,
+      content::WebContents* glic_webui_contents,
       GlicUiEmbedder::Delegate& delegate);
 
   ~GlicInactiveSidePanelUi() override;
diff --git a/chrome/browser/glic/widget/glic_window_controller_impl.cc b/chrome/browser/glic/widget/glic_window_controller_impl.cc
index a48707266..82de065 100644
--- a/chrome/browser/glic/widget/glic_window_controller_impl.cc
+++ b/chrome/browser/glic/widget/glic_window_controller_impl.cc
@@ -941,11 +941,9 @@
   window_event_observer_ =
       std::make_unique<WindowEventObserver>(this, GetGlicView());
 
-  if (!draggable_area_) {
-    // Set the draggable area to the top bar of the window.
-    GetGlicView()->SetDraggableAreas(
-        {{0, 0, GetGlicView()->width(), kDraggableAreaHeight}});
-  }
+  // Set the draggable area to the top bar of the window.
+  GetGlicView()->SetDraggableAreas(
+      {{0, 0, GetGlicView()->width(), kDraggableAreaHeight}});
 }
 
 GlicView* GlicWindowControllerImpl::GetGlicView() const {
@@ -1244,7 +1242,6 @@
   // detached, attached or currently closed.
 
   // Floating Panel State
-  draggable_area_ = std::nullopt;
   window_event_observer_.reset();
   glic_window_animator_.reset();
   glic_widget_observation_.Reset();
@@ -1522,10 +1519,7 @@
 }
 
 void GlicWindowControllerImpl::Reload() {
-  if (auto* webui_contents = host().webui_contents()) {
-    webui_contents->GetController().Reload(content::ReloadType::BYPASSING_CACHE,
-                                           /*check_for_repost=*/false);
-  }
+  host().Reload();
 }
 
 bool GlicWindowControllerImpl::IsWarmed() const {
diff --git a/chrome/browser/glic/widget/glic_window_controller_impl.h b/chrome/browser/glic/widget/glic_window_controller_impl.h
index 1ad59c9..94fa83aa 100644
--- a/chrome/browser/glic/widget/glic_window_controller_impl.h
+++ b/chrome/browser/glic/widget/glic_window_controller_impl.h
@@ -346,11 +346,6 @@
   // reset every time glic is closed but is currently cached.
   std::optional<gfx::Size> glic_size_;
 
-  // Contains the size of the draggable area zone for the glic widget.
-  // This value gets sent from the web client; if it is ever null, the draggable
-  // area will be set to a default value.
-  std::optional<gfx::Rect> draggable_area_ = std::nullopt;
-
   // Whether the widget should be user resizable, kept here in case it's
   // specified before the widget is created.
   bool user_resizable_ = true;
diff --git a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubColors.java b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubColors.java
index 6c8c388..75a05a75 100644
--- a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubColors.java
+++ b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubColors.java
@@ -19,7 +19,6 @@
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
-import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.ui.util.ColorUtils;
 import org.chromium.ui.util.ValueUtils;
@@ -96,11 +95,14 @@
             Context context, @HubColorScheme int colorScheme, boolean isGtsUpdateEnabled) {
         switch (colorScheme) {
             case HubColorScheme.DEFAULT:
-                return SurfaceColorUpdateUtils.getHubPaneSwitcherSelectedIconColor(
-                        context, /* isIncognito= */ false, isGtsUpdateEnabled);
+                return isGtsUpdateEnabled
+                        ? SemanticColorUtils.getDefaultIconColor(context)
+                        : SemanticColorUtils.getDefaultIconColorAccent1(context);
             case HubColorScheme.INCOGNITO:
-                return SurfaceColorUpdateUtils.getHubPaneSwitcherSelectedIconColor(
-                        context, /* isIncognito= */ true, isGtsUpdateEnabled);
+                return isGtsUpdateEnabled
+                        ? ContextCompat.getColor(context, R.color.default_icon_color_light)
+                        : ContextCompat.getColor(
+                                context, R.color.default_control_color_active_dark);
             default:
                 assert false;
                 return Color.TRANSPARENT;
diff --git a/chrome/browser/lookalikes/safety_tip_web_contents_observer.h b/chrome/browser/lookalikes/safety_tip_web_contents_observer.h
index b7586106..81c3231 100644
--- a/chrome/browser/lookalikes/safety_tip_web_contents_observer.h
+++ b/chrome/browser/lookalikes/safety_tip_web_contents_observer.h
@@ -18,12 +18,13 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
-#include "ui/views/widget/widget.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/lookalikes/safety_tip_message_delegate_android.h"
+#else
+#include "ui/views/widget/widget.h"
 #endif
 
 class Profile;
diff --git a/chrome/browser/media/cast_mirroring_service_host.cc b/chrome/browser/media/cast_mirroring_service_host.cc
index 19e0250..b3f5521 100644
--- a/chrome/browser/media/cast_mirroring_service_host.cc
+++ b/chrome/browser/media/cast_mirroring_service_host.cc
@@ -296,7 +296,8 @@
 
 void CastMirroringServiceHost::BindGpu(
     mojo::PendingReceiver<viz::mojom::Gpu> receiver) {
-  gpu_client_ = content::CreateGpuClient(std::move(receiver));
+  gpu_client_ = content::CreateGpuClient(
+      std::move(receiver), /*enable_extra_handles_validation=*/false);
 }
 
 void CastMirroringServiceHost::GetVideoCaptureHost(
diff --git a/chrome/browser/media/webrtc/desktop_capture_devices_util.cc b/chrome/browser/media/webrtc/desktop_capture_devices_util.cc
index e809d185..231383e 100644
--- a/chrome/browser/media/webrtc/desktop_capture_devices_util.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_devices_util.cc
@@ -29,7 +29,6 @@
 #include "content/public/common/content_features.h"
 #include "media/audio/application_loopback_device_helper.h"
 #include "media/audio/audio_device_description.h"
-#include "media/audio/audio_features.h"
 #include "media/mojo/mojom/capture_handle.mojom.h"
 #include "media/mojo/mojom/display_media_information.mojom.h"
 #include "third_party/blink/public/common/features_generated.h"
@@ -249,24 +248,22 @@
   }
 }
 
-blink::MediaStreamDevice DesktopMediaIDToAudioMediaStreamDevice(
-    std::string device_id,
-    content::DesktopMediaID::Type desktop_media_id_type,
-    blink::mojom::MediaStreamType media_stream_type) {
-  if (desktop_media_id_type == content::DesktopMediaID::TYPE_WEB_CONTENTS) {
-    return blink::MediaStreamDevice(media_stream_type, device_id, "Tab audio");
-  } else if (desktop_media_id_type == content::DesktopMediaID::TYPE_WINDOW &&
-             media::IsApplicationAudioCaptureSupported()) {
-    // TODO(crbug.com/40947205): Refactor the logic that assumes application
-    // audio is shared for all window captures (desktop_media_id.type ==
-    // content::DesktopMediaID::TYPE_WINDOW). The user should be given the
-    // option to choose whether system audio or window audio is shared.
-    return blink::MediaStreamDevice(media_stream_type, device_id,
-                                    "Application Audio");
-  } else {
-    return blink::MediaStreamDevice(media_stream_type, device_id,
-                                    "System Audio");
+std::string GetAudioMediaStreamDeviceName(
+    const content::DesktopMediaID& desktop_media_id) {
+  switch (desktop_media_id.type) {
+    case content::DesktopMediaID::TYPE_WEB_CONTENTS:
+      return "Tab audio";
+    case content::DesktopMediaID::TYPE_WINDOW:
+      return (desktop_media_id.window_audio_type ==
+              content::DesktopMediaID::AudioType::kApplication)
+                 ? "Application Audio"
+                 : "System Audio";
+    case content::DesktopMediaID::TYPE_SCREEN:
+      return "System Audio";
+    case content::DesktopMediaID::TYPE_NONE:
+      NOTREACHED();
   }
+  NOTREACHED();
 }
 
 void CreateMediaStreamCaptureIndicatorUI(
@@ -282,16 +279,9 @@
                             std::unique_ptr<content::MediaStreamUI>)>
         on_media_stream_capture_indicator_ui_created_callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-#if BUILDFLAG(IS_ANDROID)
-  std::unique_ptr<content::MediaStreamUI> capture_indicator_ui =
-      MediaCaptureDevicesDispatcher::GetInstance()
-          ->GetMediaStreamCaptureIndicator()
-          ->RegisterMediaStream(web_contents, devices);
-  std::move(on_media_stream_capture_indicator_ui_created_callback)
-      .Run(std::move(devices), std::move(capture_indicator_ui));
-#else  // !BUILDFLAG(IS_ANDROID)
-  // If required, register to display the notification for stream capture.
   std::unique_ptr<MediaStreamUI> notification_ui;
+#if !BUILDFLAG(IS_ANDROID)
+  // If required, register to display the notification for stream capture.
   if (display_notification) {
     if (media_id.type == content::DesktopMediaID::TYPE_WEB_CONTENTS) {
       content::GlobalRenderFrameHostId capturer_id;
@@ -312,6 +302,7 @@
           web_contents);
     }
   }
+#endif
 
   std::unique_ptr<content::MediaStreamUI> capture_indicator_ui =
       MediaCaptureDevicesDispatcher::GetInstance()
@@ -320,7 +311,6 @@
                                 std::move(notification_ui), application_title);
   std::move(on_media_stream_capture_indicator_ui_created_callback)
       .Run(std::move(devices), std::move(capture_indicator_ui));
-#endif
 }
 
 void OnAudioDeviceIdObtained(
@@ -347,9 +337,9 @@
   }
 
   if (audio_device_id.has_value()) {
-    blink::MediaStreamDevice audio_device =
-        DesktopMediaIDToAudioMediaStreamDevice(audio_device_id.value(),
-                                               media_id.type, audio_type);
+    blink::MediaStreamDevice audio_device(
+        audio_type, audio_device_id.value(),
+        GetAudioMediaStreamDeviceName(media_id));
     devices.audio_device = audio_device;
     devices.audio_device->display_media_info =
         DesktopMediaIDToDisplayMediaInformation(
@@ -394,11 +384,8 @@
         disable_local_echo || suppress_local_audio_playback;
     device_id = web_id.ToString();
   } else if (desktop_media_id.type == content::DesktopMediaID::TYPE_WINDOW &&
-             media::IsApplicationAudioCaptureSupported()) {
-    // TODO(crbug.com/40947205): Refactor the logic that assumes application
-    // audio is shared for all window captures (desktop_media_id.type ==
-    // content::DesktopMediaID::TYPE_WINDOW). The user should be given the
-    // option to choose whether system audio or window audio is shared.
+             desktop_media_id.window_audio_type ==
+                 content::DesktopMediaID::AudioType::kApplication) {
     base::ThreadPool::PostTaskAndReplyWithResult(
         FROM_HERE, base::BindOnce(&GetApplicationId, desktop_media_id.id),
         std::move(audio_device_id_obtained_callback));
diff --git a/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc b/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
index 96b5327..51f89b00 100644
--- a/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
+++ b/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
@@ -1087,13 +1087,8 @@
 
 #if BUILDFLAG(IS_WIN)
 class DisplayMediaAccessHandlerWindowAudioCaptureWinTest
-    : public base::test::WithFeatureOverride,
-      public DisplayMediaAccessHandlerTest {
- public:
-  DisplayMediaAccessHandlerWindowAudioCaptureWinTest()
-      : base::test::WithFeatureOverride(features::kApplicationAudioCaptureWin) {
-  }
-};
+    : public DisplayMediaAccessHandlerTest,
+      public testing::WithParamInterface<content::DesktopMediaID::AudioType> {};
 
 TEST_P(DisplayMediaAccessHandlerWindowAudioCaptureWinTest, ValidWindowId) {
   blink::mojom::MediaStreamRequestResult result;
@@ -1105,25 +1100,32 @@
   EXPECT_TRUE(window.Create(
       base::BindLambdaForTesting([&](UINT message, WPARAM wparam, LPARAM lparam,
                                      LRESULT* result) { return true; })));
-  ProcessRequest(
-      content::DesktopMediaID(content::DesktopMediaID::TYPE_WINDOW,
-                              reinterpret_cast<intptr_t>(window.hwnd()),
-                              true /* audio_share */),
-      &result, devices, true /* request_audio */);
+
+  const bool audio_share =
+      GetParam() != content::DesktopMediaID::AudioType::kNone;
+  content::DesktopMediaID fake_id(content::DesktopMediaID::TYPE_WINDOW,
+                                  reinterpret_cast<intptr_t>(window.hwnd()),
+                                  audio_share);
+  fake_id.window_audio_type = GetParam();
+  ProcessRequest(fake_id, &result, devices, audio_share);
 
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
-  EXPECT_EQ(2u, blink::CountDevices(devices));
+
+  // If the audio type is `kNone`, we should not get an audio device.
+  if (GetParam() == content::DesktopMediaID::AudioType::kNone) {
+    EXPECT_EQ(1u, blink::CountDevices(devices));
+  } else {
+    EXPECT_EQ(2u, blink::CountDevices(devices));
+    EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
+              devices.audio_device.value().type);
+  }
 
   EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
             devices.video_device.value().type);
   EXPECT_TRUE(devices.video_device.value().display_media_info);
 
-  EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
-            devices.audio_device.value().type);
-
-  if (IsParamFeatureEnabled()) {
+  if (GetParam() == content::DesktopMediaID::AudioType::kApplication) {
     EXPECT_TRUE(devices.audio_device.value().input.IsValid());
-
     // Unit tests are executed in a child process that also use the same
     // executable image (unit_tests.exe) unless the --single-process flag is
     // passed. Therefore, the application process ID should match either the
@@ -1137,7 +1139,8 @@
                                    base::Process::Current().Handle())));
 
     EXPECT_EQ("Application Audio", devices.audio_device->name);
-  } else {
+  } else if (GetParam() == content::DesktopMediaID::AudioType::kSystem) {
+    // System audio device ID and name are constant.
     EXPECT_EQ("loopback", devices.audio_device->id);
     EXPECT_EQ("System Audio", devices.audio_device->name);
   }
@@ -1146,25 +1149,44 @@
 TEST_P(DisplayMediaAccessHandlerWindowAudioCaptureWinTest, InvalidWindowId) {
   blink::mojom::MediaStreamRequestResult result;
   blink::mojom::StreamDevices devices;
-  ProcessRequest(content::DesktopMediaID(content::DesktopMediaID::TYPE_WINDOW,
-                                         content::DesktopMediaID::kFakeId,
-                                         true /* audio_share */),
-                 &result, devices, true /* request_audio */);
+  const bool audio_share =
+      GetParam() != content::DesktopMediaID::AudioType::kNone;
+  content::DesktopMediaID fake_id(content::DesktopMediaID::TYPE_WINDOW,
+                                  content::DesktopMediaID::kFakeId,
+                                  audio_share);
+  fake_id.window_audio_type = GetParam();
+  ProcessRequest(fake_id, &result, devices, audio_share);
 
   EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
-  if (IsParamFeatureEnabled()) {
-    // If the feature is enabled but the window ID is invalid, audio should not
-    // be captured.
+  // If the window ID is invalid, window audio should not be captured.
+  if (!audio_share ||
+      GetParam() == content::DesktopMediaID::AudioType::kApplication) {
     EXPECT_EQ(1u, blink::CountDevices(devices));
   } else {
     EXPECT_EQ(2u, blink::CountDevices(devices));
   }
+
   EXPECT_EQ(blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
             devices.video_device.value().type);
   EXPECT_TRUE(devices.video_device.value().display_media_info);
 }
 
-INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(
-    DisplayMediaAccessHandlerWindowAudioCaptureWinTest);
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    DisplayMediaAccessHandlerWindowAudioCaptureWinTest,
+    testing::Values(content::DesktopMediaID::AudioType::kNone,
+                    content::DesktopMediaID::AudioType::kSystem,
+                    content::DesktopMediaID::AudioType::kApplication),
+    [](const testing::TestParamInfo<content::DesktopMediaID::AudioType>& info) {
+      switch (info.param) {
+        case content::DesktopMediaID::AudioType::kNone:
+          return "NoAudio";
+        case content::DesktopMediaID::AudioType::kSystem:
+          return "SystemAudio";
+        case content::DesktopMediaID::AudioType::kApplication:
+          return "ApplicationAudio";
+      }
+      NOTREACHED();
+    });
 
 #endif  // BUILDFLAG(IS_WIN)
diff --git a/chrome/browser/navigation_predictor/preloading_model_executor_unittest.cc b/chrome/browser/navigation_predictor/preloading_model_executor_unittest.cc
index 42c0e85..fee9496 100644
--- a/chrome/browser/navigation_predictor/preloading_model_executor_unittest.cc
+++ b/chrome/browser/navigation_predictor/preloading_model_executor_unittest.cc
@@ -32,18 +32,20 @@
                            .AppendASCII("navigation_predictor")
                            .AppendASCII("test")
                            .AppendASCII("preloading_heuristics.tflite");
-    execution_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
+    task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
         {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
     model_executor_ = std::make_unique<PreloadingModelExecutor>();
     model_executor_->InitializeAndMoveToExecutionThread(
         /*model_inference_timeout=*/std::nullopt,
         optimization_guide::proto::OPTIMIZATION_TARGET_OMNIBOX_URL_SCORING,
-        execution_task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
+        /*model_loading_task_runner=*/task_runner_,
+        /*execution_task_runner=*/task_runner_,
+        base::SequencedTaskRunner::GetCurrentDefault());
   }
 
   void TearDown() override {
     // Destroy model executor.
-    execution_task_runner_->DeleteSoon(FROM_HERE, std::move(model_executor_));
+    task_runner_->DeleteSoon(FROM_HERE, std::move(model_executor_));
     RunUntilIdle();
   }
 
@@ -53,13 +55,13 @@
   base::test::ScopedFeatureList scoped_feature_list_;
   base::test::TaskEnvironment task_environment_;
   base::FilePath model_file_path_;
-  scoped_refptr<base::SequencedTaskRunner> execution_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
   std::unique_ptr<PreloadingModelExecutor> model_executor_;
 };
 
 TEST_F(PreloadingModelExecutorTest, ExecuteModel) {
   // Update model file.
-  execution_task_runner_->PostTask(
+  task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
           &optimization_guide::ModelExecutor<ModelOutput,
@@ -80,7 +82,7 @@
           run_loop.get());
   base::TimeTicks now = base::TimeTicks::Now();
   ModelInput input = std::vector<float>(/*count=*/17, /*value=*/0.0);
-  execution_task_runner_->PostTask(
+  task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&optimization_guide::ModelExecutor<
                                     ModelOutput, ModelInput>::SendForExecution,
                                 model_executor_->GetWeakPtrForExecutionThread(),
diff --git a/chrome/browser/notifications/mac/notification_utils.cc b/chrome/browser/notifications/mac/notification_utils.cc
index ccae9cfc..83272b9 100644
--- a/chrome/browser/notifications/mac/notification_utils.cc
+++ b/chrome/browser/notifications/mac/notification_utils.cc
@@ -67,7 +67,8 @@
                 static_cast<NotificationHandler::Type>(info->meta->type),
                 std::move(info->meta->origin_url),
                 std::move(info->meta->id->id), std::move(action_index),
-                std::move(info->reply), /*by_user=*/true, base::DoNothing());
+                std::move(info->reply), /*by_user=*/true,
+                /*is_suspicious=*/false, base::DoNothing());
   profile_manager->LoadProfile(
       NotificationPlatformBridge::GetProfileBaseNameFromProfileId(
           info->meta->id->profile->id),
diff --git a/chrome/browser/notifications/non_persistent_notification_handler.cc b/chrome/browser/notifications/non_persistent_notification_handler.cc
index deddf0e..b22a504c 100644
--- a/chrome/browser/notifications/non_persistent_notification_handler.cc
+++ b/chrome/browser/notifications/non_persistent_notification_handler.cc
@@ -129,7 +129,8 @@
 void NonPersistentNotificationHandler::DisableNotifications(
     Profile* profile,
     const GURL& origin,
-    const std::optional<std::string>& notification_id) {
+    const std::optional<std::string>& notification_id,
+    const std::optional<bool>& is_suspicious) {
   permissions::PermissionUmaUtil::ScopedRevocationReporter
       scoped_revocation_reporter(
           profile, origin, origin, ContentSettingsType::NOTIFICATIONS,
diff --git a/chrome/browser/notifications/non_persistent_notification_handler.h b/chrome/browser/notifications/non_persistent_notification_handler.h
index 664df498..4151a62 100644
--- a/chrome/browser/notifications/non_persistent_notification_handler.h
+++ b/chrome/browser/notifications/non_persistent_notification_handler.h
@@ -31,10 +31,10 @@
                const std::optional<int>& action_index,
                const std::optional<std::u16string>& reply,
                base::OnceClosure completed_closure) override;
-  void DisableNotifications(
-      Profile* profile,
-      const GURL& origin,
-      const std::optional<std::string>& notification_id) override;
+  void DisableNotifications(Profile* profile,
+                            const GURL& origin,
+                            const std::optional<std::string>& notification_id,
+                            const std::optional<bool>& is_suspicious) override;
   void OpenSettings(Profile* profile, const GURL& origin) override;
 
  private:
diff --git a/chrome/browser/notifications/notification_display_service_impl.cc b/chrome/browser/notifications/notification_display_service_impl.cc
index 9509359..8a4f880 100644
--- a/chrome/browser/notifications/notification_display_service_impl.cc
+++ b/chrome/browser/notifications/notification_display_service_impl.cc
@@ -141,6 +141,7 @@
     const std::optional<int>& action_index,
     const std::optional<std::u16string>& reply,
     const std::optional<bool>& by_user,
+    const std::optional<bool>& is_suspicious,
     base::OnceClosure on_completed_cb) {
   NotificationHandler* handler = GetNotificationHandler(notification_type);
   DCHECK(handler);
@@ -164,7 +165,8 @@
         observer.OnNotificationClosed(notification_id);
       break;
     case NotificationOperation::kDisablePermission:
-      handler->DisableNotifications(profile_, origin, notification_id);
+      handler->DisableNotifications(profile_, origin, notification_id,
+                                    is_suspicious);
       break;
     case NotificationOperation::kSettings:
       handler->OpenSettings(profile_, origin);
@@ -307,6 +309,7 @@
     const std::optional<int>& action_index,
     const std::optional<std::u16string>& reply,
     const std::optional<bool>& by_user,
+    const std::optional<bool>& is_suspicious,
     base::OnceClosure on_completed_cb,
     Profile* profile) {
   base::UmaHistogramBoolean("Notifications.LoadProfileResult",
@@ -321,7 +324,7 @@
       NotificationDisplayServiceImpl::GetForProfile(profile);
   display_service->ProcessNotificationOperation(
       operation, notification_type, origin, notification_id, action_index,
-      reply, by_user, std::move(on_completed_cb));
+      reply, by_user, is_suspicious, std::move(on_completed_cb));
 }
 
 void NotificationDisplayServiceImpl::SetBlockersForTesting(
diff --git a/chrome/browser/notifications/notification_display_service_impl.h b/chrome/browser/notifications/notification_display_service_impl.h
index b0fd66c..8b5910c 100644
--- a/chrome/browser/notifications/notification_display_service_impl.h
+++ b/chrome/browser/notifications/notification_display_service_impl.h
@@ -64,6 +64,7 @@
       const std::optional<int>& action_index,
       const std::optional<std::u16string>& reply,
       const std::optional<bool>& by_user,
+      const std::optional<bool>& is_suspicious,
       base::OnceClosure on_completed_cb);
 
   // Registers an implementation object to handle notification operations
@@ -96,6 +97,7 @@
                                     const std::optional<int>& action_index,
                                     const std::optional<std::u16string>& reply,
                                     const std::optional<bool>& by_user,
+                                    const std::optional<bool>& is_suspicious,
                                     base::OnceClosure on_completed_cb,
                                     Profile* profile);
 
diff --git a/chrome/browser/notifications/notification_display_service_impl_unittest.cc b/chrome/browser/notifications/notification_display_service_impl_unittest.cc
index 16bad131..e83a21f 100644
--- a/chrome/browser/notifications/notification_display_service_impl_unittest.cc
+++ b/chrome/browser/notifications/notification_display_service_impl_unittest.cc
@@ -359,7 +359,7 @@
       NotificationOperation::kClick,
       NotificationHandler::Type::NOTIFICATIONS_MUTED, /*origin=*/GURL(),
       kMuteNotificationId, /*action_index=*/0, /*reply=*/std::nullopt,
-      /*by_user=*/true,
+      /*by_user=*/true, /*is_suspicious=*/false,
       base::BindOnce([](base::RunLoop* looper) { looper->Quit(); }, &run_loop));
   run_loop.Run();
 
diff --git a/chrome/browser/notifications/notification_handler.cc b/chrome/browser/notifications/notification_handler.cc
index 4cc5ff2..3ba7934a 100644
--- a/chrome/browser/notifications/notification_handler.cc
+++ b/chrome/browser/notifications/notification_handler.cc
@@ -31,7 +31,8 @@
 void NotificationHandler::DisableNotifications(
     Profile* profile,
     const GURL& origin,
-    const std::optional<std::string>& notification_id) {
+    const std::optional<std::string>& notification_id,
+    const std::optional<bool>& is_suspicious) {
   NOTREACHED();
 }
 
diff --git a/chrome/browser/notifications/notification_handler.h b/chrome/browser/notifications/notification_handler.h
index 5b42eea..ea08f4e 100644
--- a/chrome/browser/notifications/notification_handler.h
+++ b/chrome/browser/notifications/notification_handler.h
@@ -67,11 +67,13 @@
 
   // Called when notifications of the given origin have to be disabled. The
   // |notification_id| is included on Android and indicates the notification
-  // that led to further notifications being disabled.
+  // that led to further notifications being disabled. The |is_suspicious|
+  // parameter is used for logging metrics.
   virtual void DisableNotifications(
       Profile* profile,
       const GURL& origin,
-      const std::optional<std::string>& notification_id);
+      const std::optional<std::string>& notification_id,
+      const std::optional<bool>& is_suspicious);
 
   // Called when the settings page for the given origin has to be opened.
   virtual void OpenSettings(Profile* profile, const GURL& origin);
diff --git a/chrome/browser/notifications/notification_permission_browsertest.cc b/chrome/browser/notifications/notification_permission_browsertest.cc
index b808e1d..fda8aaf 100644
--- a/chrome/browser/notifications/notification_permission_browsertest.cc
+++ b/chrome/browser/notifications/notification_permission_browsertest.cc
@@ -359,7 +359,8 @@
   std::unique_ptr<NotificationHandler> handler =
       std::make_unique<NonPersistentNotificationHandler>();
   handler->DisableNotifications(browser()->profile(), TesterUrl(),
-                                /*notification_id=*/std::nullopt);
+                                /*notification_id=*/std::nullopt,
+                                /*is_suspicious=*/false);
 
   const auto action_entries = ukm_recorder.GetEntriesByName("Permission");
   ASSERT_EQ(2u, action_entries.size());
diff --git a/chrome/browser/notifications/notification_platform_bridge_android.cc b/chrome/browser/notifications/notification_platform_bridge_android.cc
index a365954..9fb8123 100644
--- a/chrome/browser/notifications/notification_platform_bridge_android.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_android.cc
@@ -15,6 +15,7 @@
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/functional/bind.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -200,7 +201,7 @@
           &NotificationDisplayServiceImpl::ProfileLoadedCallback,
           NotificationOperation::kClick, notification_type, origin,
           notification_id, std::move(action_index), std::move(reply),
-          std::nullopt /* by_user */,
+          std::nullopt /* by_user */, std::nullopt /* is_suspicious */,
           base::BindOnce(
               &NotificationPlatformBridgeAndroid::OnNotificationProcessed,
               weak_factory_.GetWeakPtr(), notification_id)));
@@ -243,7 +244,7 @@
           &NotificationDisplayServiceImpl::ProfileLoadedCallback,
           NotificationOperation::kClose, notification_type, GURL(origin),
           notification_id, std::nullopt /* action index */,
-          std::nullopt /* reply */, by_user,
+          std::nullopt /* reply */, by_user, std::nullopt /* is_suspicious */,
           base::BindOnce(
               &NotificationPlatformBridgeAndroid::OnNotificationProcessed,
               weak_factory_.GetWeakPtr(), notification_id)));
@@ -255,7 +256,8 @@
     jint java_notification_type,
     std::string& origin,
     std::string& profile_id,
-    jboolean incognito) {
+    jboolean incognito,
+    jboolean is_suspicious) {
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   DCHECK(profile_manager);
 
@@ -268,7 +270,8 @@
                      NotificationOperation::kDisablePermission,
                      notification_type, GURL(origin), notification_id,
                      std::nullopt /* action index */, std::nullopt /* reply */,
-                     std::nullopt /* by_user */, base::DoNothing()));
+                     std::nullopt /* by_user */, is_suspicious,
+                     base::DoNothing()));
 }
 
 void NotificationPlatformBridgeAndroid::SetIsSuspiciousParameterForTesting(
@@ -294,7 +297,7 @@
                      NotificationHandler::Type::WEB_PERSISTENT, GURL(origin),
                      notification_id, std::nullopt /* action index */,
                      std::nullopt /* reply */, std::nullopt /* by_user */,
-                     base::DoNothing()));
+                     std::nullopt /* is_suspicious */, base::DoNothing()));
 }
 
 void NotificationPlatformBridgeAndroid::OnReportWarnedNotificationAsSpam(
@@ -313,7 +316,7 @@
                      NotificationHandler::Type::WEB_PERSISTENT, GURL(origin),
                      notification_id, std::nullopt /* action index */,
                      std::nullopt /* reply */, std::nullopt /* by_user */,
-                     base::DoNothing()));
+                     std::nullopt /* is_suspicious */, base::DoNothing()));
 }
 
 void NotificationPlatformBridgeAndroid::OnReportUnwarnedNotificationAsSpam(
@@ -332,7 +335,7 @@
                      NotificationHandler::Type::WEB_PERSISTENT, GURL(origin),
                      notification_id, std::nullopt /* action index */,
                      std::nullopt /* reply */, std::nullopt /* by_user */,
-                     base::DoNothing()));
+                     std::nullopt /* is_suspicious */, base::DoNothing()));
 }
 
 void NotificationPlatformBridgeAndroid::OnNotificationShowOriginalNotification(
@@ -351,7 +354,7 @@
                      NotificationHandler::Type::WEB_PERSISTENT, GURL(origin),
                      /*notification_id=*/"", std::nullopt /* action index */,
                      std::nullopt /* reply */, std::nullopt /* by_user */,
-                     base::DoNothing()));
+                     std::nullopt /* is_suspicious */, base::DoNothing()));
 }
 
 void NotificationPlatformBridgeAndroid::OnNotificationAlwaysAllowFromOrigin(
diff --git a/chrome/browser/notifications/notification_platform_bridge_android.h b/chrome/browser/notifications/notification_platform_bridge_android.h
index ecaf849..a4e12b7 100644
--- a/chrome/browser/notifications/notification_platform_bridge_android.h
+++ b/chrome/browser/notifications/notification_platform_bridge_android.h
@@ -73,13 +73,13 @@
 
   // Called by the Java implementation when the user commits to unsubscribing
   // from notification from this origin.
-  void OnNotificationDisablePermission(
-      JNIEnv* env,
-      std::string& otification_id,
-      jint java_notification_type,
-      std::string& origin,
-      std::string& profile_id,
-      jboolean incognito);
+  void OnNotificationDisablePermission(JNIEnv* env,
+                                       std::string& otification_id,
+                                       jint java_notification_type,
+                                       std::string& origin,
+                                       std::string& profile_id,
+                                       jboolean incognito,
+                                       jboolean is_suspicious);
 
   // Called by Java tests for testing both suspicious and non-suspicious
   // notification behaviour when showing warnings for suspicious notifications
diff --git a/chrome/browser/notifications/notification_platform_bridge_chromeos.cc b/chrome/browser/notifications/notification_platform_bridge_chromeos.cc
index 05a7517..44987b8 100644
--- a/chrome/browser/notifications/notification_platform_bridge_chromeos.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_chromeos.cc
@@ -119,7 +119,7 @@
             NotificationOperation::kClose, notification->type(),
             notification->notification().origin_url(),
             notification->original_id(), std::nullopt, std::nullopt, by_user,
-            base::DoNothing());
+            std::nullopt, base::DoNothing());
   }
   active_notifications_.erase(iter);
 }
@@ -138,7 +138,7 @@
             NotificationOperation::kClick, notification->type(),
             notification->notification().origin_url(),
             notification->original_id(), std::nullopt, std::nullopt,
-            std::nullopt, base::DoNothing());
+            std::nullopt, std::nullopt, base::DoNothing());
   }
 }
 
@@ -158,7 +158,7 @@
             NotificationOperation::kClick, notification->type(),
             notification->notification().origin_url(),
             notification->original_id(), button_index, reply, std::nullopt,
-            base::DoNothing());
+            std::nullopt, base::DoNothing());
   }
 }
 
@@ -176,7 +176,7 @@
             NotificationOperation::kSettings, notification->type(),
             notification->notification().origin_url(),
             notification->original_id(), std::nullopt, std::nullopt,
-            std::nullopt, base::DoNothing());
+            std::nullopt, std::nullopt, base::DoNothing());
   }
 }
 
@@ -192,7 +192,7 @@
           NotificationOperation::kDisablePermission, notification->type(),
           notification->notification().origin_url(),
           notification->original_id(), std::nullopt, std::nullopt, std::nullopt,
-          base::DoNothing());
+          std::nullopt, base::DoNothing());
 }
 
 ProfileNotification* NotificationPlatformBridgeChromeOs::GetProfileNotification(
diff --git a/chrome/browser/notifications/notification_platform_bridge_linux.cc b/chrome/browser/notifications/notification_platform_bridge_linux.cc
index d5a7ac1..b274627 100644
--- a/chrome/browser/notifications/notification_platform_bridge_linux.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_linux.cc
@@ -245,7 +245,8 @@
       is_incognito,
       base::BindOnce(&NotificationDisplayServiceImpl::ProfileLoadedCallback,
                      operation, notification_type, origin, notification_id,
-                     action_index, reply, by_user, base::DoNothing()));
+                     action_index, reply, by_user, /*is_suspicious=*/false,
+                     base::DoNothing()));
 }
 
 bool WriteImageFile(scoped_refptr<base::RefCountedMemory> image,
diff --git a/chrome/browser/notifications/notification_platform_bridge_linux_unittest.cc b/chrome/browser/notifications/notification_platform_bridge_linux_unittest.cc
index 06d7e52..f829ebf 100644
--- a/chrome/browser/notifications/notification_platform_bridge_linux_unittest.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_linux_unittest.cc
@@ -344,7 +344,8 @@
                        const std::string& notification_id,
                        const std::optional<int>& action_index,
                        const std::optional<std::u16string>& reply,
-                       const std::optional<bool>& by_user) {
+                       const std::optional<bool>& by_user,
+                       const std::optional<bool>& is_suspicious) {
     last_operation_ = operation;
     last_action_index_ = action_index;
     last_reply_ = reply;
diff --git a/chrome/browser/notifications/notification_platform_bridge_message_center.cc b/chrome/browser/notifications/notification_platform_bridge_message_center.cc
index cdc971b..a8d5c17 100644
--- a/chrome/browser/notifications/notification_platform_bridge_message_center.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_message_center.cc
@@ -43,7 +43,8 @@
         ->ProcessNotificationOperation(
             NotificationOperation::kSettings, notification_type_,
             notification_.origin_url(), notification_.id(), std::nullopt,
-            std::nullopt, std::nullopt /* by_user */, base::DoNothing());
+            std::nullopt, std::nullopt /* by_user */,
+            std::nullopt /* is_suspicious */, base::DoNothing());
   }
 
   void DisableNotification() override {
@@ -52,7 +53,8 @@
             NotificationOperation::kDisablePermission, notification_type_,
             notification_.origin_url(), notification_.id(),
             std::nullopt /* action_index */, std::nullopt /* reply */,
-            std::nullopt /* by_user */, base::DoNothing());
+            std::nullopt /* by_user */, std::nullopt /* is_suspicious */,
+            base::DoNothing());
   }
 
   void Close(bool by_user) override {
@@ -61,7 +63,7 @@
             NotificationOperation::kClose, notification_type_,
             notification_.origin_url(), notification_.id(),
             std::nullopt /* action_index */, std::nullopt /* reply */, by_user,
-            base::DoNothing());
+            std::nullopt /* is_suspicious */, base::DoNothing());
   }
 
   void Click(const std::optional<int>& button_index,
@@ -70,7 +72,8 @@
         ->ProcessNotificationOperation(
             NotificationOperation::kClick, notification_type_,
             notification_.origin_url(), notification_.id(), button_index, reply,
-            std::nullopt /* by_user */, base::DoNothing());
+            std::nullopt /* by_user */, std::nullopt /* is_suspicious */,
+            base::DoNothing());
   }
 
  protected:
diff --git a/chrome/browser/notifications/notification_platform_bridge_win.cc b/chrome/browser/notifications/notification_platform_bridge_win.cc
index 5cefdf0..219c0338 100644
--- a/chrome/browser/notifications/notification_platform_bridge_win.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_win.cc
@@ -138,7 +138,8 @@
       incognito,
       base::BindOnce(&NotificationDisplayServiceImpl::ProfileLoadedCallback,
                      operation, notification_type, origin, notification_id,
-                     action_index, reply, by_user, base::DoNothing()));
+                     action_index, reply, by_user, /*is_suspicious=*/false,
+                     base::DoNothing()));
 }
 
 GetSettingPolicy ConvertSettingPolicy(
diff --git a/chrome/browser/notifications/notification_platform_bridge_win_interactive_uitest.cc b/chrome/browser/notifications/notification_platform_bridge_win_interactive_uitest.cc
index dd7ad485..c7f94ff5 100644
--- a/chrome/browser/notifications/notification_platform_bridge_win_interactive_uitest.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_win_interactive_uitest.cc
@@ -148,7 +148,8 @@
                        const std::string& notification_id,
                        const std::optional<int>& action_index,
                        const std::optional<std::u16string>& reply,
-                       const std::optional<bool>& by_user) {
+                       const std::optional<bool>& by_user,
+                       const std::optional<bool>& is_suspicious) {
     last_operation_ = operation;
     last_notification_type_ = notification_type;
     last_origin_ = origin;
diff --git a/chrome/browser/notifications/persistent_notification_handler.cc b/chrome/browser/notifications/persistent_notification_handler.cc
index aa0b56d..345d9331 100644
--- a/chrome/browser/notifications/persistent_notification_handler.cc
+++ b/chrome/browser/notifications/persistent_notification_handler.cc
@@ -35,6 +35,7 @@
 #include "components/permissions/permission_uma_util.h"
 #include "components/permissions/permission_util.h"
 #include "components/safe_browsing/content/browser/notification_content_detection/notification_content_detection_constants.h"
+#include "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h"
 #include "components/safe_browsing/core/common/features.h"
 #include "components/site_engagement/content/site_engagement_service.h"
 #include "content/public/browser/browser_thread.h"
@@ -300,7 +301,8 @@
 void PersistentNotificationHandler::DisableNotifications(
     Profile* profile,
     const GURL& origin,
-    const std::optional<std::string>& notification_id) {
+    const std::optional<std::string>& notification_id,
+    const std::optional<bool>& is_suspicious) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   permissions::PermissionUmaUtil::ScopedRevocationReporter
       scoped_revocation_reporter(
@@ -331,6 +333,15 @@
       safe_browsing::MaybeLogSuspiciousNotificationUnsubscribeUkm(
           hcsm, origin, notification_id.value(), profile);
     }
+    if (is_suspicious.has_value()) {
+      safe_browsing::SafeBrowsingMetricsCollector::
+          LogSafeBrowsingNotificationRevocationSourceHistogram(
+              is_suspicious.value()
+                  ? safe_browsing::NotificationRevocationSource::
+                        kSuspiciousWarningOneTapUnsubscribe
+                  : safe_browsing::NotificationRevocationSource::
+                        kStandardOneTapUnsubscribe);
+    }
 #endif
   }
 }
diff --git a/chrome/browser/notifications/persistent_notification_handler.h b/chrome/browser/notifications/persistent_notification_handler.h
index 8ad2543..66395af 100644
--- a/chrome/browser/notifications/persistent_notification_handler.h
+++ b/chrome/browser/notifications/persistent_notification_handler.h
@@ -48,10 +48,10 @@
                const std::optional<int>& action_index,
                const std::optional<std::u16string>& reply,
                base::OnceClosure completed_closure) override;
-  void DisableNotifications(
-      Profile* profile,
-      const GURL& origin,
-      const std::optional<std::string>& notification_id) override;
+  void DisableNotifications(Profile* profile,
+                            const GURL& origin,
+                            const std::optional<std::string>& notification_id,
+                            const std::optional<bool>& is_suspicious) override;
   void OpenSettings(Profile* profile, const GURL& origin) override;
   void ReportNotificationAsSafe(const std::string& notification_id,
                                 const GURL& url,
diff --git a/chrome/browser/notifications/persistent_notification_handler_unittest.cc b/chrome/browser/notifications/persistent_notification_handler_unittest.cc
index 8514ffe..9e6ec613 100644
--- a/chrome/browser/notifications/persistent_notification_handler_unittest.cc
+++ b/chrome/browser/notifications/persistent_notification_handler_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/thread_pool.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/test_future.h"
 #include "build/build_config.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -35,6 +36,7 @@
 #include "components/safe_browsing/content/browser/notification_content_detection/notification_content_detection_constants.h"
 #include "components/safe_browsing/content/browser/notification_content_detection/notification_content_detection_service.h"
 #include "components/safe_browsing/content/browser/notification_content_detection/test_model_observer_tracker.h"
+#include "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h"
 #include "components/safe_browsing/core/common/features.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/ukm/test_ukm_recorder.h"
@@ -226,6 +228,9 @@
 }
 
 TEST_F(PersistentNotificationHandlerTest, DisableNotifications) {
+#if BUILDFLAG(IS_ANDROID)
+  base::HistogramTester histograms;
+#endif
   ukm::TestAutoSetUkmRecorder test_ukm_recorder;
   std::unique_ptr<NotificationPermissionContext> permission_context =
       std::make_unique<NotificationPermissionContext>(profile_.get());
@@ -269,7 +274,8 @@
 #endif
   handler->DisableNotifications(
       profile_.get(), origin_,
-      /*notification_id=*/"non-suspicious-notification-id");
+      /*notification_id=*/"non-suspicious-notification-id",
+      /*is_suspicious=*/false);
 
   // Disabling the permission should set
   // `ARE_SUSPICIOUS_NOTIFICATIONS_ALLOWLISTED_BY_USER` to false.
@@ -311,6 +317,13 @@
   // should not log a suspicious score.
   EXPECT_FALSE(
       test_ukm_recorder.EntryHasMetric(ukm_entries[0], "SuspiciousScore"));
+  // Log histogram when notifications are disabledwithout previously receiving a
+  // warning.
+  histograms.ExpectUniqueSample(
+      "SafeBrowsing.NotificationRevocationSource",
+      static_cast<int>(safe_browsing::NotificationRevocationSource::
+                           kStandardOneTapUnsubscribe),
+      1);
 #else
   EXPECT_EQ(0u, ukm_entries.size());
 #endif
@@ -318,7 +331,8 @@
 
 #if BUILDFLAG(IS_ANDROID)
 TEST_F(PersistentNotificationHandlerTest,
-       DisableNotificationAfterWarningLogsUKM) {
+       DisableNotificationAfterWarningLogsMetrics) {
+  base::HistogramTester histograms;
   ukm::TestAutoSetUkmRecorder test_ukm_recorder;
   std::unique_ptr<NotificationPermissionContext> permission_context =
       std::make_unique<NotificationPermissionContext>(profile_.get());
@@ -337,7 +351,8 @@
 
   std::unique_ptr<NotificationHandler> handler =
       std::make_unique<PersistentNotificationHandler>();
-  handler->DisableNotifications(profile_.get(), origin_, suspicious_id);
+  handler->DisableNotifications(profile_.get(), origin_, suspicious_id,
+                                /*is_suspicious=*/true);
   task_environment_.RunUntilIdle();
 
   // Disabling notifications after a warning was shown should log the UKM.
@@ -353,6 +368,12 @@
   // should not log a suspicious score.
   EXPECT_FALSE(
       test_ukm_recorder.EntryHasMetric(ukm_entries[0], "SuspiciousScore"));
+  // Log histogram when notifications are disabled after receiving a warning.
+  histograms.ExpectUniqueSample(
+      "SafeBrowsing.NotificationRevocationSource",
+      static_cast<int>(safe_browsing::NotificationRevocationSource::
+                           kSuspiciousWarningOneTapUnsubscribe),
+      1);
 }
 #endif
 
diff --git a/chrome/browser/notifications/stub_notification_display_service.cc b/chrome/browser/notifications/stub_notification_display_service.cc
index c21775c3..dcf2340 100644
--- a/chrome/browser/notifications/stub_notification_display_service.cc
+++ b/chrome/browser/notifications/stub_notification_display_service.cc
@@ -249,19 +249,20 @@
     const std::optional<int>& action_index,
     const std::optional<std::u16string>& reply,
     const std::optional<bool>& by_user,
+    const std::optional<bool>& is_suspicious,
     base::OnceClosure on_complete_cb) {
   if (process_notification_operation_delegate_) {
     // TODO(b/375547360): run `on_complete_cb` when notification processing
     // finishes.
-    process_notification_operation_delegate_.Run(operation, notification_type,
-                                                 origin, notification_id,
-                                                 action_index, reply, by_user);
+    process_notification_operation_delegate_.Run(
+        operation, notification_type, origin, notification_id, action_index,
+        reply, by_user, is_suspicious);
     return;
   }
 
   NotificationDisplayServiceImpl::ProcessNotificationOperation(
       operation, notification_type, origin, notification_id, action_index,
-      reply, by_user, std::move(on_complete_cb));
+      reply, by_user, is_suspicious, std::move(on_complete_cb));
 }
 
 StubNotificationDisplayService::NotificationData::NotificationData(
diff --git a/chrome/browser/notifications/stub_notification_display_service.h b/chrome/browser/notifications/stub_notification_display_service.h
index bfbfd47..3846e5d79 100644
--- a/chrome/browser/notifications/stub_notification_display_service.h
+++ b/chrome/browser/notifications/stub_notification_display_service.h
@@ -40,7 +40,8 @@
       const std::string& notification_id,
       const std::optional<int>& action_index,
       const std::optional<std::u16string>& reply,
-      const std::optional<bool>& by_user)>
+      const std::optional<bool>& by_user,
+      const std::optional<bool>& is_suspicious)>
       ProcessNotificationOperationCallback;
 
   explicit StubNotificationDisplayService(Profile* profile);
@@ -112,6 +113,7 @@
                                     const std::optional<int>& action_index,
                                     const std::optional<std::u16string>& reply,
                                     const std::optional<bool>& by_user,
+                                    const std::optional<bool>& is_suspicious,
                                     base::OnceClosure on_complete_cb) override;
 
  private:
diff --git a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationConfigManager.java b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationConfigManager.java
index 1b8524db..f1b3cbb3 100644
--- a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationConfigManager.java
+++ b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationConfigManager.java
@@ -344,7 +344,9 @@
      * @param context The current Activity context. It is themed and can provide the correct color.
      */
     public @ColorInt int getBackgroundColor(Context context) {
-        if (!mIsInitialized) return getDefaultBackgroundColor(context);
+        if (!mIsInitialized || mBackgroundColor == NtpCustomizationConfigManager.COLOR_NOT_SET) {
+            return getDefaultBackgroundColor(context);
+        }
 
         return mBackgroundColor;
     }
diff --git a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationUtils.java b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationUtils.java
index 516ca19c..053e212 100644
--- a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationUtils.java
+++ b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationUtils.java
@@ -46,7 +46,6 @@
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.ntp_customization.theme.BackgroundImageInfo;
-import org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
 import org.chromium.chrome.browser.tab.Tab;
@@ -57,6 +56,8 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.concurrent.Executor;
 
@@ -68,13 +69,14 @@
         NtpBackgroundImageType.DEFAULT,
         NtpBackgroundImageType.IMAGE_FROM_DISK,
         NtpBackgroundImageType.CHROME_COLOR,
-        NtpBackgroundImageType.CHROME_THEME
+        NtpBackgroundImageType.THEME_COLLECTION
     })
+    @Retention(RetentionPolicy.SOURCE)
     public @interface NtpBackgroundImageType {
         int DEFAULT = 0;
         int IMAGE_FROM_DISK = 1;
         int CHROME_COLOR = 2;
-        int CHROME_THEME = 3;
+        int THEME_COLLECTION = 3;
         int NUM_ENTRIES = 4;
     }
 
@@ -545,29 +547,6 @@
         defaultGoogleLogoDrawable.setTint(tintColor);
     }
 
-    /**
-     * Returns the corresponding {@link NTPThemeBottomSheetSection} for a given {@link
-     * NtpBackgroundImageType}.
-     *
-     * @param imageType The background image type.
-     */
-    public static @NTPThemeBottomSheetSection int getSectionForBackgroundImageType(
-            @NtpBackgroundImageType int imageType) {
-        switch (imageType) {
-            case NtpBackgroundImageType.DEFAULT:
-                return NTPThemeBottomSheetSection.CHROME_DEFAULT;
-            case NtpBackgroundImageType.IMAGE_FROM_DISK:
-                return NTPThemeBottomSheetSection.UPLOAD_AN_IMAGE;
-            case NtpBackgroundImageType.CHROME_COLOR:
-                return NTPThemeBottomSheetSection.CHROME_COLORS;
-            case NtpBackgroundImageType.CHROME_THEME:
-                return NTPThemeBottomSheetSection.THEME_COLLECTIONS;
-            default:
-                assert false : "image type not supported!";
-                return NTPThemeBottomSheetSection.NUM_ENTRIES;
-        }
-    }
-
     public static void resetSharedPreferenceForTesting() {
         SharedPreferencesManager prefsManager = ChromeSharedPreferences.getInstance();
         prefsManager.removeKey(ChromePreferenceKeys.NTP_CUSTOMIZATION_BACKGROUND_IMAGE_TYPE);
diff --git a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpBackgroundImageView.java b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpBackgroundImageView.java
index 32f2d726..1155ea3 100644
--- a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpBackgroundImageView.java
+++ b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpBackgroundImageView.java
@@ -4,8 +4,8 @@
 
 package org.chromium.chrome.browser.ntp_customization.theme;
 
-import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.CHROME_THEME;
 import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.IMAGE_FROM_DISK;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.THEME_COLLECTION;
 
 import android.app.Activity;
 import android.content.Context;
@@ -21,7 +21,7 @@
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
-import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils;
+import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType;
 import org.chromium.components.browser_ui.widget.displaystyle.DisplayStyleObserver;
 import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
 
@@ -49,12 +49,12 @@
     public void setBackground(
             Bitmap originalBitmap,
             @Nullable BackgroundImageInfo backgroundImageInfo,
-            @NtpCustomizationUtils.NtpBackgroundImageType int backgroundType) {
+            @NtpBackgroundImageType int backgroundType) {
         mOriginalBitmap = originalBitmap;
         mBackgroundImageType = backgroundType;
 
         switch (backgroundType) {
-            case CHROME_THEME:
+            case THEME_COLLECTION:
                 setScaleType(ImageView.ScaleType.CENTER_CROP);
                 setImageBitmap(originalBitmap);
                 break;
diff --git a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeBottomSheetView.java b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeBottomSheetView.java
index 38613bd..4356937 100644
--- a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeBottomSheetView.java
+++ b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeBottomSheetView.java
@@ -5,10 +5,10 @@
 package org.chromium.chrome.browser.ntp_customization.theme;
 
 import static org.chromium.build.NullUtil.assumeNonNull;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.CHROME_COLORS;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.CHROME_DEFAULT;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.THEME_COLLECTIONS;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.UPLOAD_AN_IMAGE;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.CHROME_COLOR;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.DEFAULT;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.IMAGE_FROM_DISK;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.THEME_COLLECTION;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
@@ -22,13 +22,13 @@
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType;
 import org.chromium.chrome.browser.ntp_customization.R;
-import org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection;
 
 /** The view of the "New tab page appearance" bottom sheet. */
 @NullMarked
 public class NtpThemeBottomSheetView extends ScrollView {
-    private NtpThemeListItemView mChromeDefaultSection;
+    private NtpThemeListItemView mDefaultSection;
     private NtpThemeListItemView mUploadImageSection;
     private NtpThemeListItemView mChromeColorsSection;
     private NtpThemeListItemView mThemeCollectionsSection;
@@ -45,13 +45,13 @@
         LayoutInflater inflater = LayoutInflater.from(getContext());
 
         // Inflate and add each theme section.
-        mChromeDefaultSection =
+        mDefaultSection =
                 (NtpThemeListItemView)
                         inflater.inflate(
                                 R.layout.ntp_customization_theme_list_chrome_default_item_layout,
                                 container,
                                 false);
-        container.addView(mChromeDefaultSection);
+        container.addView(mDefaultSection);
 
         mUploadImageSection =
                 (NtpThemeListItemView)
@@ -79,21 +79,21 @@
     }
 
     void destroy() {
-        for (int i = 0; i < NTPThemeBottomSheetSection.NUM_ENTRIES; i++) {
+        for (int i = 0; i < NtpBackgroundImageType.NUM_ENTRIES; i++) {
             NtpThemeListItemView child = assumeNonNull(getItemBySectionType(i));
             child.destroy();
         }
     }
 
     void setSectionTrailingIconVisibility(
-            @NTPThemeBottomSheetSection int sectionType, boolean visible) {
+            @NtpBackgroundImageType int sectionType, boolean visible) {
         NtpThemeListItemView ntpThemeListItemView =
                 assumeNonNull(getItemBySectionType(sectionType));
         ntpThemeListItemView.setTrailingIconVisibility(visible);
     }
 
     void setSectionOnClickListener(
-            @NTPThemeBottomSheetSection int sectionType, OnClickListener onClickListener) {
+            @NtpBackgroundImageType int sectionType, OnClickListener onClickListener) {
         NtpThemeListItemView ntpThemeListItemView =
                 assumeNonNull(getItemBySectionType(sectionType));
         ntpThemeListItemView.setOnClickListener(onClickListener);
@@ -105,7 +105,7 @@
         Drawable secondaryDrawable =
                 ContextCompat.getDrawable(getContext(), drawableSourcePair.second);
         NtpThemeListItemView themeCollectionsItemView =
-                assumeNonNull(getItemBySectionType(THEME_COLLECTIONS));
+                assumeNonNull(getItemBySectionType(THEME_COLLECTION));
         NtpThemeListThemeCollectionItemIconView themeCollectionsItemIconView =
                 themeCollectionsItemView.findViewById(
                         org.chromium.chrome.browser.ntp_customization.R.id.leading_icon);
@@ -113,16 +113,15 @@
                 new Pair<>(primaryDrawable, secondaryDrawable));
     }
 
-    @Nullable NtpThemeListItemView getItemBySectionType(
-            @NTPThemeBottomSheetSection int sectionType) {
+    @Nullable NtpThemeListItemView getItemBySectionType(@NtpBackgroundImageType int sectionType) {
         switch (sectionType) {
-            case CHROME_DEFAULT:
-                return mChromeDefaultSection;
-            case UPLOAD_AN_IMAGE:
+            case DEFAULT:
+                return mDefaultSection;
+            case IMAGE_FROM_DISK:
                 return mUploadImageSection;
-            case CHROME_COLORS:
+            case CHROME_COLOR:
                 return mChromeColorsSection;
-            case THEME_COLLECTIONS:
+            case THEME_COLLECTION:
                 return mThemeCollectionsSection;
             default:
                 assert false : "Section type not supported!";
diff --git a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeCoordinator.java b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeCoordinator.java
index 0bb43b1e..c80e747 100644
--- a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeCoordinator.java
+++ b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeCoordinator.java
@@ -10,7 +10,6 @@
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.support.annotation.IntDef;
 import android.view.LayoutInflater;
 
 import androidx.activity.ComponentActivity;
@@ -27,30 +26,9 @@
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /** Coordinator for the NTP appearance settings bottom sheet in the NTP customization. */
 @NullMarked
 public class NtpThemeCoordinator {
-
-    /** NTP appearance sections that are shown in the theme bottom sheet. */
-    @IntDef({
-        NTPThemeBottomSheetSection.CHROME_DEFAULT,
-        NTPThemeBottomSheetSection.UPLOAD_AN_IMAGE,
-        NTPThemeBottomSheetSection.CHROME_COLORS,
-        NTPThemeBottomSheetSection.THEME_COLLECTIONS,
-        NTPThemeBottomSheetSection.NUM_ENTRIES
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface NTPThemeBottomSheetSection {
-        int CHROME_DEFAULT = 0;
-        int UPLOAD_AN_IMAGE = 1;
-        int CHROME_COLORS = 2;
-        int THEME_COLLECTIONS = 3;
-        int NUM_ENTRIES = 4;
-    }
-
     private final Context mContext;
     private final BottomSheetDelegate mBottomSheetDelegate;
     private final Runnable mDismissBottomSheetRunnable;
diff --git a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeMediator.java b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeMediator.java
index 48fe24b..b445425 100644
--- a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeMediator.java
+++ b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeMediator.java
@@ -4,12 +4,12 @@
 
 package org.chromium.chrome.browser.ntp_customization.theme;
 
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.CHROME_COLOR;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.DEFAULT;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.IMAGE_FROM_DISK;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.THEME_COLLECTION;
 import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.launchUriActivity;
 import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationViewProperties.BACK_PRESS_HANDLER;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.CHROME_COLORS;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.CHROME_DEFAULT;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.THEME_COLLECTIONS;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.UPLOAD_AN_IMAGE;
 import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeProperty.IS_SECTION_TRAILING_ICON_VISIBLE;
 import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeProperty.LEADING_ICON_FOR_THEME_COLLECTIONS;
 import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeProperty.LEARN_MORE_BUTTON_CLICK_LISTENER;
@@ -35,7 +35,6 @@
 import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils;
 import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType;
 import org.chromium.chrome.browser.ntp_customization.R;
-import org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection;
 import org.chromium.chrome.browser.ntp_customization.theme.chrome_colors.NtpChromeColorsCoordinator;
 import org.chromium.chrome.browser.ntp_customization.theme.theme_collections.NtpThemeCollectionsCoordinator;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -125,35 +124,35 @@
                                 // image.
                                 ShareImageFileUtils.getBitmapFromUriAsync(
                                         mContext, uri, mOnImageSelectedCallback);
-                                updateTrailingIconVisibilityForSectionType(UPLOAD_AN_IMAGE);
+                                updateTrailingIconVisibilityForSectionType(IMAGE_FROM_DISK);
                             });
         }
 
         mThemePropertyModel.set(
                 SECTION_ON_CLICK_LISTENER,
-                new Pair<>(CHROME_DEFAULT, this::handleChromeDefaultSectionClick));
+                new Pair<>(DEFAULT, this::handleChromeDefaultSectionClick));
         mThemePropertyModel.set(
                 SECTION_ON_CLICK_LISTENER,
-                new Pair<>(UPLOAD_AN_IMAGE, this::handleUploadAnImageSectionClick));
+                new Pair<>(IMAGE_FROM_DISK, this::handleUploadAnImageSectionClick));
         mThemePropertyModel.set(
                 SECTION_ON_CLICK_LISTENER,
-                new Pair<>(CHROME_COLORS, this::handleChromeColorsSectionClick));
+                new Pair<>(CHROME_COLOR, this::handleChromeColorsSectionClick));
         mThemePropertyModel.set(
                 SECTION_ON_CLICK_LISTENER,
-                new Pair<>(THEME_COLLECTIONS, this::handleThemeCollectionsSectionClick));
+                new Pair<>(THEME_COLLECTION, this::handleThemeCollectionsSectionClick));
     }
 
     /**
      * Updates the visibility of the trailing icon for each theme section. The icon is made visible
      * for the section that matches {@code sectionType}, and hidden for all other sections.
      *
-     * @param sectionType The {@link NTPThemeBottomSheetSection} to show the trailing icon for.
+     * @param sectionType The {@link NtpBackgroundImageType} to show the trailing icon for.
      */
     private void updateTrailingIconVisibilityForSectionType(
-            @NTPThemeBottomSheetSection int sectionType) {
-        for (int i = 0; i < NTPThemeBottomSheetSection.NUM_ENTRIES; i++) {
-            if (i == THEME_COLLECTIONS) {
-                if (sectionType != THEME_COLLECTIONS && mNtpThemeCollectionsCoordinator != null) {
+            @NtpBackgroundImageType int sectionType) {
+        for (int i = 0; i < NtpBackgroundImageType.NUM_ENTRIES; i++) {
+            if (i == THEME_COLLECTION) {
+                if (sectionType != THEME_COLLECTION && mNtpThemeCollectionsCoordinator != null) {
                     mNtpThemeCollectionsCoordinator.clearThemeCollectionSelection();
                 }
                 continue;
@@ -182,18 +181,16 @@
 
     @VisibleForTesting
     void handleChromeDefaultSectionClick(View view) {
-        updateTrailingIconVisibilityForSectionType(CHROME_DEFAULT);
+        updateTrailingIconVisibilityForSectionType(DEFAULT);
 
         @NtpBackgroundImageType
         int currentBackgroundType = mNtpCustomizationConfigManager.getBackgroundImageType();
-        if (currentBackgroundType != NtpBackgroundImageType.DEFAULT) {
+        if (currentBackgroundType != DEFAULT) {
             // We need to update the app's theme when a customized background color is removed.
             mBottomSheetDelegate.onNewColorSelected(/* isDifferentColor= */ true);
         }
         mNtpCustomizationConfigManager.onBackgroundColorChanged(
-                mContext,
-                /* colorInfo= */ null,
-                NtpCustomizationUtils.NtpBackgroundImageType.DEFAULT);
+                mContext, /* colorInfo= */ null, DEFAULT);
     }
 
     @VisibleForTesting
@@ -212,7 +209,7 @@
                             mBottomSheetDelegate,
                             mCallbackController.makeCancelable(
                                     () -> {
-                                        updateTrailingIconVisibilityForSectionType(CHROME_COLORS);
+                                        updateTrailingIconVisibilityForSectionType(CHROME_COLOR);
                                     }));
         }
         mBottomSheetDelegate.showBottomSheet(BottomSheetType.CHROME_COLORS);
@@ -229,7 +226,7 @@
                             mCallbackController.makeCancelable(
                                     () -> {
                                         updateTrailingIconVisibilityForSectionType(
-                                                THEME_COLLECTIONS);
+                                                THEME_COLLECTION);
                                     }));
         }
         mBottomSheetDelegate.showBottomSheet(BottomSheetType.THEME_COLLECTIONS);
@@ -242,11 +239,8 @@
 
     /** Sets the initial visibility of the trailing icon based on the current theme settings. */
     private void initTrailingIcon() {
-        @NtpCustomizationUtils.NtpBackgroundImageType
-        int imageType = NtpCustomizationUtils.getNtpBackgroundImageType();
-        @NTPThemeBottomSheetSection
-        int section = NtpCustomizationUtils.getSectionForBackgroundImageType(imageType);
-        updateTrailingIconVisibilityForSectionType(section);
+        @NtpBackgroundImageType int imageType = NtpCustomizationUtils.getNtpBackgroundImageType();
+        updateTrailingIconVisibilityForSectionType(imageType);
     }
 
     void setNtpThemeCollectionsCoordinatorForTesting(
diff --git a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/chrome_colors/NtpChromeColorsCoordinator.java b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/chrome_colors/NtpChromeColorsCoordinator.java
index 6a6bede..fbe5cdc5 100644
--- a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/chrome_colors/NtpChromeColorsCoordinator.java
+++ b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/chrome_colors/NtpChromeColorsCoordinator.java
@@ -33,6 +33,7 @@
 import org.chromium.chrome.browser.ntp_customization.BottomSheetDelegate;
 import org.chromium.chrome.browser.ntp_customization.NtpCustomizationConfigManager;
 import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils;
+import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType;
 import org.chromium.chrome.browser.ntp_customization.R;
 import org.chromium.ui.text.EmptyTextWatcher;
 import org.chromium.ui.widget.ButtonCompat;
@@ -152,9 +153,7 @@
                         || ntpThemeColorInfo.primaryColor != mPrimaryColor.intValue());
         NtpCustomizationConfigManager.getInstance()
                 .onBackgroundColorChanged(
-                        mContext,
-                        ntpThemeColorInfo,
-                        NtpCustomizationUtils.NtpBackgroundImageType.CHROME_COLOR);
+                        mContext, ntpThemeColorInfo, NtpBackgroundImageType.CHROME_COLOR);
         mOnChromeColorSelectedCallback.run();
     }
 
diff --git a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationConfigManagerUnitTest.java b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationConfigManagerUnitTest.java
index 2982701..8043e9a 100644
--- a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationConfigManagerUnitTest.java
+++ b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationConfigManagerUnitTest.java
@@ -95,7 +95,7 @@
 
     @Test
     public void testOnBackgroundChanged_persistsStateAndNotifiesListener() {
-        int initialBackgroundImageType = NtpBackgroundImageType.CHROME_THEME;
+        int initialBackgroundImageType = NtpBackgroundImageType.THEME_COLLECTION;
         mNtpCustomizationConfigManager.setBackgroundImageTypeForTesting(initialBackgroundImageType);
         mNtpCustomizationConfigManager.addListener(mListener, mContext);
 
diff --git a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationUtilsUnitTest.java b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationUtilsUnitTest.java
index ee470cd..5c0356c0 100644
--- a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationUtilsUnitTest.java
+++ b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/NtpCustomizationUtilsUnitTest.java
@@ -18,6 +18,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.IMAGE_FROM_DISK;
 import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.getBackground;
 
 import android.content.Context;
@@ -125,8 +126,7 @@
         assertEquals(
                 NtpBackgroundImageType.DEFAULT, NtpCustomizationUtils.getNtpBackgroundImageType());
 
-        @NtpBackgroundImageType
-        int imageType = NtpCustomizationUtils.NtpBackgroundImageType.IMAGE_FROM_DISK;
+        @NtpBackgroundImageType int imageType = IMAGE_FROM_DISK;
         NtpCustomizationUtils.setNtpBackgroundImageType(imageType);
 
         assertEquals(imageType, NtpCustomizationUtils.getNtpBackgroundImageType());
@@ -272,7 +272,7 @@
     @Test
     @EnableFeatures(ChromeFeatureList.NEW_TAB_PAGE_CUSTOMIZATION_V2)
     public void testGetPrimaryColorFromCustomizedThemeColor_colorSetWithImage() {
-        NtpCustomizationUtils.setNtpBackgroundImageType(NtpBackgroundImageType.IMAGE_FROM_DISK);
+        NtpCustomizationUtils.setNtpBackgroundImageType(IMAGE_FROM_DISK);
         NtpCustomizationUtils.setCustomizedPrimaryColor(Color.BLUE);
 
         assertEquals(
@@ -370,8 +370,7 @@
         verify(mDrawable, never()).setTint(anyInt());
 
         // Verifies that color white is set for customized background images.
-        customizationConfigManager.setBackgroundImageTypeForTesting(
-                NtpBackgroundImageType.IMAGE_FROM_DISK);
+        customizationConfigManager.setBackgroundImageTypeForTesting(IMAGE_FROM_DISK);
         NtpCustomizationUtils.setTintForDefaultGoogleLogo(mContext, mDrawable);
         verify(mDrawable).setTint(eq(Color.WHITE));
 
@@ -380,8 +379,7 @@
         clearInvocations(mDrawable);
 
         // Verifies that color white is set for customized background images.
-        customizationConfigManager.setBackgroundImageTypeForTesting(
-                NtpBackgroundImageType.IMAGE_FROM_DISK);
+        customizationConfigManager.setBackgroundImageTypeForTesting(IMAGE_FROM_DISK);
         NtpCustomizationUtils.setTintForDefaultGoogleLogo(mContext, mDrawable);
         verify(mDrawable).setTint(eq(Color.WHITE));
 
diff --git a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinatorUnitTest.java b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinatorUnitTest.java
index 2917419..b33c6d2 100644
--- a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinatorUnitTest.java
+++ b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinatorUnitTest.java
@@ -276,7 +276,8 @@
         // Verifies that observers are NOT added again when a customized background type is changed.
         clearInvocations(mNtpTab);
         clearInvocations(mLayoutStateProvider);
-        setBackgroundType(NtpBackgroundImageType.CHROME_COLOR, NtpBackgroundImageType.CHROME_THEME);
+        setBackgroundType(
+                NtpBackgroundImageType.CHROME_COLOR, NtpBackgroundImageType.THEME_COLLECTION);
         verify(mNtpTab, never()).addObserver(any(TabObserver.class));
         verify(mLayoutStateProvider, never())
                 .addObserver(any(LayoutStateProvider.LayoutStateObserver.class));
@@ -284,7 +285,7 @@
         assertNotNull(mTopInsetCoordinator.getTrackingTabForTesting());
 
         // Verifies that observers are removed when the customized background is removed.
-        setBackgroundType(NtpBackgroundImageType.CHROME_THEME, NtpBackgroundImageType.DEFAULT);
+        setBackgroundType(NtpBackgroundImageType.THEME_COLLECTION, NtpBackgroundImageType.DEFAULT);
         verify(mNtpTab, times(2)).removeObserver(any(TabObserver.class));
         verify(mLayoutStateProvider)
                 .removeObserver(any(LayoutStateProvider.LayoutStateObserver.class));
@@ -316,12 +317,13 @@
         // Verifies that retriggerOnApplyWindowInsets() isn't called again when the customized
         // background type is changed.
         clearInvocations(mInsetObserver);
-        setBackgroundType(NtpBackgroundImageType.CHROME_COLOR, NtpBackgroundImageType.CHROME_THEME);
+        setBackgroundType(
+                NtpBackgroundImageType.CHROME_COLOR, NtpBackgroundImageType.THEME_COLLECTION);
         verify(mInsetObserver, never()).retriggerOnApplyWindowInsets();
 
         // Verifies that retriggerOnApplyWindowInsets() is called when the customized background is
         // removed.
-        setBackgroundType(NtpBackgroundImageType.CHROME_THEME, NtpBackgroundImageType.DEFAULT);
+        setBackgroundType(NtpBackgroundImageType.THEME_COLLECTION, NtpBackgroundImageType.DEFAULT);
         verify(mInsetObserver).retriggerOnApplyWindowInsets();
 
         // Verifies that retriggerOnApplyWindowInsets() isn't called again when the background type
diff --git a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpBackgroundImageViewUnitTest.java b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpBackgroundImageViewUnitTest.java
index 580f10ec..31e81320 100644
--- a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpBackgroundImageViewUnitTest.java
+++ b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpBackgroundImageViewUnitTest.java
@@ -9,8 +9,8 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
-import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.CHROME_THEME;
 import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.IMAGE_FROM_DISK;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.THEME_COLLECTION;
 
 import android.app.Activity;
 import android.graphics.Bitmap;
@@ -60,7 +60,7 @@
 
     @Test
     public void testSetBackground_withChromeTheme() {
-        mBackgroundImageViewSpy.setBackground(mBitmap, null, CHROME_THEME);
+        mBackgroundImageViewSpy.setBackground(mBitmap, null, THEME_COLLECTION);
 
         verify(mBackgroundImageViewSpy).setScaleType(eq(ImageView.ScaleType.CENTER_CROP));
         verify(mBackgroundImageViewSpy).setImageBitmap(eq(mBitmap));
diff --git a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeBottomSheetViewUnitTest.java b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeBottomSheetViewUnitTest.java
index 56fb698..157f6695 100644
--- a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeBottomSheetViewUnitTest.java
+++ b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeBottomSheetViewUnitTest.java
@@ -11,10 +11,10 @@
 import static org.mockito.Mockito.when;
 import static org.robolectric.Shadows.shadowOf;
 
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.CHROME_COLORS;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.CHROME_DEFAULT;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.THEME_COLLECTIONS;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.UPLOAD_AN_IMAGE;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.CHROME_COLOR;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.DEFAULT;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.IMAGE_FROM_DISK;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.THEME_COLLECTION;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
@@ -45,7 +45,7 @@
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
-    @Mock private NtpThemeListItemView mChromeDefaultSection;
+    @Mock private NtpThemeListItemView mDefaultSection;
     @Mock private NtpThemeListItemView mUploadAnImageSection;
     @Mock private NtpThemeListItemView mChromeColorsSection;
     @Mock private NtpThemeListItemView mThemeCollectionsSection;
@@ -64,13 +64,12 @@
 
         mNtpThemeBottomSheetView = spy(new NtpThemeBottomSheetView(mContext, null));
 
-        when(mNtpThemeBottomSheetView.getItemBySectionType(CHROME_DEFAULT))
-                .thenReturn(mChromeDefaultSection);
-        when(mNtpThemeBottomSheetView.getItemBySectionType(UPLOAD_AN_IMAGE))
+        when(mNtpThemeBottomSheetView.getItemBySectionType(DEFAULT)).thenReturn(mDefaultSection);
+        when(mNtpThemeBottomSheetView.getItemBySectionType(IMAGE_FROM_DISK))
                 .thenReturn(mUploadAnImageSection);
-        when(mNtpThemeBottomSheetView.getItemBySectionType(CHROME_COLORS))
+        when(mNtpThemeBottomSheetView.getItemBySectionType(CHROME_COLOR))
                 .thenReturn(mChromeColorsSection);
-        when(mNtpThemeBottomSheetView.getItemBySectionType(THEME_COLLECTIONS))
+        when(mNtpThemeBottomSheetView.getItemBySectionType(THEME_COLLECTION))
                 .thenReturn(mThemeCollectionsSection);
         when(mThemeCollectionsSection.findViewById(R.id.leading_icon))
                 .thenReturn(mThemeCollectionsItemIconView);
@@ -80,7 +79,7 @@
     public void testDestroy() {
         mNtpThemeBottomSheetView.destroy();
 
-        verify(mChromeDefaultSection).destroy();
+        verify(mDefaultSection).destroy();
         verify(mUploadAnImageSection).destroy();
         verify(mChromeColorsSection).destroy();
         verify(mThemeCollectionsSection).destroy();
@@ -88,19 +87,19 @@
 
     @Test
     public void testSetSectionTrailingIconVisibility() {
-        mNtpThemeBottomSheetView.setSectionTrailingIconVisibility(CHROME_DEFAULT, true);
-        verify(mChromeDefaultSection).setTrailingIconVisibility(eq(true));
+        mNtpThemeBottomSheetView.setSectionTrailingIconVisibility(DEFAULT, true);
+        verify(mDefaultSection).setTrailingIconVisibility(eq(true));
 
-        mNtpThemeBottomSheetView.setSectionTrailingIconVisibility(UPLOAD_AN_IMAGE, false);
+        mNtpThemeBottomSheetView.setSectionTrailingIconVisibility(IMAGE_FROM_DISK, false);
         verify(mUploadAnImageSection).setTrailingIconVisibility(eq(false));
     }
 
     @Test
     public void testSetSectionOnClickListener() {
-        mNtpThemeBottomSheetView.setSectionOnClickListener(CHROME_COLORS, mOnClickListener);
+        mNtpThemeBottomSheetView.setSectionOnClickListener(CHROME_COLOR, mOnClickListener);
         verify(mChromeColorsSection).setOnClickListener(mOnClickListener);
 
-        mNtpThemeBottomSheetView.setSectionOnClickListener(THEME_COLLECTIONS, mOnClickListener);
+        mNtpThemeBottomSheetView.setSectionOnClickListener(THEME_COLLECTION, mOnClickListener);
         verify(mThemeCollectionsSection).setOnClickListener(mOnClickListener);
     }
 
diff --git a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeCoordinatorUnitTest.java b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeCoordinatorUnitTest.java
index cff27875..0df62dd 100644
--- a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeCoordinatorUnitTest.java
+++ b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeCoordinatorUnitTest.java
@@ -28,6 +28,7 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.ntp_customization.BottomSheetDelegate;
+import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType;
 import org.chromium.chrome.browser.ntp_customization.R;
 import org.chromium.chrome.browser.profiles.Profile;
 
@@ -62,9 +63,7 @@
     @Test
     public void testRegisterBottomSheetLayout() {
         verify(mBottomSheetDelegate)
-                .registerBottomSheetLayout(
-                        eq(NtpThemeCoordinator.NTPThemeBottomSheetSection.THEME_COLLECTIONS),
-                        any());
+                .registerBottomSheetLayout(eq(NtpBackgroundImageType.THEME_COLLECTION), any());
     }
 
     @Test
diff --git a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeMediatorUnitTest.java b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeMediatorUnitTest.java
index f0a00086..16fd5f5 100644
--- a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeMediatorUnitTest.java
+++ b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeMediatorUnitTest.java
@@ -17,10 +17,10 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.CHROME_COLOR;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.DEFAULT;
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.IMAGE_FROM_DISK;
 import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationViewProperties.BACK_PRESS_HANDLER;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.CHROME_COLORS;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.CHROME_DEFAULT;
-import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection.UPLOAD_AN_IMAGE;
 import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeProperty.IS_SECTION_TRAILING_ICON_VISIBLE;
 import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeProperty.LEADING_ICON_FOR_THEME_COLLECTIONS;
 import static org.chromium.chrome.browser.ntp_customization.theme.NtpThemeProperty.LEARN_MORE_BUTTON_CLICK_LISTENER;
@@ -131,10 +131,7 @@
 
         mMediator.handleChromeDefaultSectionClick(mView);
         verify(mNtpCustomizationConfigManager)
-                .onBackgroundColorChanged(
-                        eq(mContext),
-                        eq(null),
-                        eq(NtpCustomizationUtils.NtpBackgroundImageType.DEFAULT));
+                .onBackgroundColorChanged(eq(mContext), eq(null), eq(DEFAULT));
     }
 
     @Test
@@ -175,11 +172,11 @@
         mMediator.handleChromeDefaultSectionClick(mView);
 
         verify(mThemePropertyModel)
-                .set(eq(IS_SECTION_TRAILING_ICON_VISIBLE), eq(new Pair<>(CHROME_DEFAULT, true)));
+                .set(eq(IS_SECTION_TRAILING_ICON_VISIBLE), eq(new Pair<>(DEFAULT, true)));
         verify(mThemePropertyModel)
-                .set(eq(IS_SECTION_TRAILING_ICON_VISIBLE), eq(new Pair<>(UPLOAD_AN_IMAGE, false)));
+                .set(eq(IS_SECTION_TRAILING_ICON_VISIBLE), eq(new Pair<>(IMAGE_FROM_DISK, false)));
         verify(mThemePropertyModel)
-                .set(eq(IS_SECTION_TRAILING_ICON_VISIBLE), eq(new Pair<>(CHROME_COLORS, false)));
+                .set(eq(IS_SECTION_TRAILING_ICON_VISIBLE), eq(new Pair<>(CHROME_COLOR, false)));
     }
 
     @Test
@@ -220,11 +217,10 @@
 
     @Test
     public void testInitTrailingIcon() {
-        NtpCustomizationUtils.setNtpBackgroundImageType(
-                NtpCustomizationUtils.NtpBackgroundImageType.CHROME_COLOR);
+        NtpCustomizationUtils.setNtpBackgroundImageType(CHROME_COLOR);
         createMediator(true);
         verify(mThemePropertyModel)
-                .set(eq(IS_SECTION_TRAILING_ICON_VISIBLE), eq(new Pair<>(CHROME_COLORS, true)));
+                .set(eq(IS_SECTION_TRAILING_ICON_VISIBLE), eq(new Pair<>(CHROME_COLOR, true)));
         NtpCustomizationUtils.resetSharedPreferenceForTesting();
     }
 
diff --git a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeViewBinderUnitTest.java b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeViewBinderUnitTest.java
index 797f1808..f1e6378 100644
--- a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeViewBinderUnitTest.java
+++ b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeViewBinderUnitTest.java
@@ -6,6 +6,8 @@
 
 import static org.mockito.Mockito.verify;
 
+import static org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundImageType.DEFAULT;
+
 import android.content.Context;
 import android.util.Pair;
 import android.view.ContextThemeWrapper;
@@ -26,7 +28,6 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.ntp_customization.R;
-import org.chromium.chrome.browser.ntp_customization.theme.NtpThemeCoordinator.NTPThemeBottomSheetSection;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
@@ -75,15 +76,13 @@
         PropertyModelChangeProcessor.create(
                 mModel, mNtpThemeBottomSheetView, NtpThemeViewBinder::bindThemeBottomSheet);
 
-        Pair<Integer, Boolean> pair = new Pair<>(NTPThemeBottomSheetSection.CHROME_DEFAULT, true);
+        Pair<Integer, Boolean> pair = new Pair<>(DEFAULT, true);
         mModel.set(NtpThemeProperty.IS_SECTION_TRAILING_ICON_VISIBLE, pair);
-        verify(mNtpThemeBottomSheetView)
-                .setSectionTrailingIconVisibility(NTPThemeBottomSheetSection.CHROME_DEFAULT, true);
+        verify(mNtpThemeBottomSheetView).setSectionTrailingIconVisibility(DEFAULT, true);
 
-        pair = new Pair<>(NTPThemeBottomSheetSection.CHROME_DEFAULT, false);
+        pair = new Pair<>(DEFAULT, false);
         mModel.set(NtpThemeProperty.IS_SECTION_TRAILING_ICON_VISIBLE, pair);
-        verify(mNtpThemeBottomSheetView)
-                .setSectionTrailingIconVisibility(NTPThemeBottomSheetSection.CHROME_DEFAULT, false);
+        verify(mNtpThemeBottomSheetView).setSectionTrailingIconVisibility(DEFAULT, false);
     }
 
     @Test
@@ -91,12 +90,9 @@
         PropertyModelChangeProcessor.create(
                 mModel, mNtpThemeBottomSheetView, NtpThemeViewBinder::bindThemeBottomSheet);
 
-        final Pair<Integer, View.OnClickListener> pair =
-                new Pair<>(NTPThemeBottomSheetSection.CHROME_DEFAULT, mOnClickListener);
+        final Pair<Integer, View.OnClickListener> pair = new Pair<>(DEFAULT, mOnClickListener);
         mModel.set(NtpThemeProperty.SECTION_ON_CLICK_LISTENER, pair);
-        verify(mNtpThemeBottomSheetView)
-                .setSectionOnClickListener(
-                        NTPThemeBottomSheetSection.CHROME_DEFAULT, mOnClickListener);
+        verify(mNtpThemeBottomSheetView).setSectionOnClickListener(DEFAULT, mOnClickListener);
     }
 
     @Test
diff --git a/chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h b/chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h
index 47bfc6b..5c49ced 100644
--- a/chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h
+++ b/chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h
@@ -103,6 +103,7 @@
               AddObserverForOptimizationTargetModel,
               (optimization_guide::proto::OptimizationTarget,
                const std::optional<optimization_guide::proto::Any>&,
+               scoped_refptr<base::SequencedTaskRunner>,
                optimization_guide::OptimizationTargetModelObserver*),
               (override));
   MOCK_METHOD(void,
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index 659eac0b..f3ea112 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -373,9 +373,10 @@
 void OptimizationGuideKeyedService::AddObserverForOptimizationTargetModel(
     optimization_guide::proto::OptimizationTarget optimization_target,
     const std::optional<optimization_guide::proto::Any>& model_metadata,
+    scoped_refptr<base::SequencedTaskRunner> model_task_runner,
     optimization_guide::OptimizationTargetModelObserver* observer) {
   GetPredictionManager()->AddObserverForOptimizationTargetModel(
-      optimization_target, model_metadata, observer);
+      optimization_target, model_metadata, model_task_runner, observer);
 }
 
 void OptimizationGuideKeyedService::RemoveObserverForOptimizationTargetModel(
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
index f00a66e9..0d74111 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
@@ -138,6 +138,7 @@
   void AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OptimizationTarget optimization_target,
       const std::optional<optimization_guide::proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       optimization_guide::OptimizationTargetModelObserver* observer) override;
   void RemoveObserverForOptimizationTargetModel(
       optimization_guide::proto::OptimizationTarget optimization_target,
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
index bd1a3e3..dddae13 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
@@ -12,6 +12,7 @@
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
+#include "base/task/thread_pool.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_run_loop_timeout.h"
@@ -202,7 +203,10 @@
     OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
         ->AddObserverForOptimizationTargetModel(
             optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
-            std::nullopt, model_file_observer);
+            std::nullopt,
+            base::ThreadPool::CreateSequencedTaskRunner(
+                {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
+            model_file_observer);
   }
 
   PredictionManager* GetPredictionManager() {
@@ -429,7 +433,10 @@
         profile ? profile : browser()->profile())
         ->AddObserverForOptimizationTargetModel(
             proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
-            /*model_metadata=*/std::nullopt, model_file_observer_.get());
+            /*model_metadata=*/std::nullopt,
+            base::ThreadPool::CreateSequencedTaskRunner(
+                {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
+            model_file_observer_.get());
   }
 
  private:
@@ -883,7 +890,10 @@
   OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
       ->AddObserverForOptimizationTargetModel(
           proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
-          /*model_metadata=*/std::nullopt, &model_file_observer);
+          /*model_metadata=*/std::nullopt,
+          base::ThreadPool::CreateSequencedTaskRunner(
+              {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
+          &model_file_observer);
 
   run_loop.Run();
 }
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model_store_browsertest.cc b/chrome/browser/optimization_guide/prediction/prediction_model_store_browsertest.cc
index 12ee3ac..ab63b26 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_model_store_browsertest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_model_store_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
+#include "base/task/thread_pool.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_run_loop_timeout.h"
 #include "base/test/task_environment.h"
@@ -132,7 +133,10 @@
     OptimizationGuideKeyedServiceFactory::GetForProfile(profile)
         ->AddObserverForOptimizationTargetModel(
             proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
-            /*model_metadata=*/std::nullopt, model_file_observer);
+            /*model_metadata=*/std::nullopt,
+            base::ThreadPool::CreateSequencedTaskRunner(
+                {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
+            model_file_observer);
   }
 
   // Registers |model_file_observer| for model updates from the optimization
diff --git a/chrome/browser/page_content_annotations/BUILD.gn b/chrome/browser/page_content_annotations/BUILD.gn
index 67cfc0e6..a7cd62fd 100644
--- a/chrome/browser/page_content_annotations/BUILD.gn
+++ b/chrome/browser/page_content_annotations/BUILD.gn
@@ -4,6 +4,8 @@
 
 source_set("extraction_service") {
   sources = [
+    "page_content_cache_handler.cc",
+    "page_content_cache_handler.h",
     "page_content_extraction_service.h",
     "page_content_extraction_service_factory.h",
     "page_content_extraction_types.h",
@@ -14,6 +16,8 @@
     "//chrome/browser/profiles:profile",
     "//components/keyed_service/core",
     "//components/optimization_guide/proto:optimization_guide_proto",
+    "//components/page_content_annotations/core",
+    "//content/public/browser",
   ]
 }
 
diff --git a/chrome/browser/page_content_annotations/android/page_content_extraction_tab_model_observer_android.cc b/chrome/browser/page_content_annotations/android/page_content_extraction_tab_model_observer_android.cc
new file mode 100644
index 0000000..ecf39b64
--- /dev/null
+++ b/chrome/browser/page_content_annotations/android/page_content_extraction_tab_model_observer_android.cc
@@ -0,0 +1,54 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_content_annotations/android/page_content_extraction_tab_model_observer_android.h"
+
+#include "chrome/browser/android/tab_android.h"
+#include "chrome/browser/page_content_annotations/page_content_cache_handler.h"
+#include "chrome/browser/page_content_annotations/page_content_extraction_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/page.h"
+#include "content/public/browser/web_contents.h"
+
+namespace page_content_annotations {
+
+PageContentExtractionTabModelObserverAndroid::
+    PageContentExtractionTabModelObserverAndroid(
+        Profile* profile,
+        PageContentExtractionService* service)
+    : profile_(profile), service_(service) {
+  TabModelList::AddObserver(this);
+  for (TabModel* tab_model : TabModelList::models()) {
+    OnTabModelAdded(tab_model);
+  }
+}
+
+PageContentExtractionTabModelObserverAndroid::
+    ~PageContentExtractionTabModelObserverAndroid() {
+  TabModelList::RemoveObserver(this);
+}
+
+void PageContentExtractionTabModelObserverAndroid::OnTabModelAdded(
+    TabModel* tab_model) {
+  if (tab_model->GetProfile() != profile_) {
+    return;
+  }
+  tab_model_observations_.AddObservation(tab_model);
+}
+
+void PageContentExtractionTabModelObserverAndroid::OnTabModelRemoved(
+    TabModel* tab_model) {
+  if (tab_model->GetProfile() != profile_) {
+    return;
+  }
+  tab_model_observations_.RemoveObservation(tab_model);
+}
+
+void PageContentExtractionTabModelObserverAndroid::OnFinishingTabClosure(
+    TabAndroid* tab,
+    TabModel::TabClosingSource source) {
+  service_->OnTabClosed(tab->GetAndroidId());
+}
+
+}  // namespace page_content_annotations
diff --git a/chrome/browser/page_content_annotations/android/page_content_extraction_tab_model_observer_android.h b/chrome/browser/page_content_annotations/android/page_content_extraction_tab_model_observer_android.h
new file mode 100644
index 0000000..459d8dd
--- /dev/null
+++ b/chrome/browser/page_content_annotations/android/page_content_extraction_tab_model_observer_android.h
@@ -0,0 +1,59 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PAGE_CONTENT_ANNOTATIONS_ANDROID_PAGE_CONTENT_EXTRACTION_TAB_MODEL_OBSERVER_ANDROID_H_
+#define CHROME_BROWSER_PAGE_CONTENT_ANNOTATIONS_ANDROID_PAGE_CONTENT_EXTRACTION_TAB_MODEL_OBSERVER_ANDROID_H_
+
+#include "base/scoped_multi_source_observation.h"
+#include "base/scoped_observation.h"
+#include "base/supports_user_data.h"
+#include "chrome/browser/ui/android/tab_model/tab_model.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_list_observer.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_observer.h"
+
+class Profile;
+class TabAndroid;
+
+namespace page_content_annotations {
+
+class PageContentExtractionService;
+
+// Observes TabModelList and TabModel to provide tab-related notifications
+// for page content annotations. Obervser is one per profile, and ignores any
+// observations from incognito and other profiles.
+class PageContentExtractionTabModelObserverAndroid
+    : public TabModelListObserver,
+      public TabModelObserver,
+      public base::SupportsUserData::Data {
+ public:
+  explicit PageContentExtractionTabModelObserverAndroid(
+      Profile* profile,
+      PageContentExtractionService* service);
+  ~PageContentExtractionTabModelObserverAndroid() override;
+
+  PageContentExtractionTabModelObserverAndroid(
+      const PageContentExtractionTabModelObserverAndroid&) = delete;
+  PageContentExtractionTabModelObserverAndroid& operator=(
+      const PageContentExtractionTabModelObserverAndroid&) = delete;
+
+  // TabModelListObserver:
+  void OnTabModelAdded(TabModel* tab_model) override;
+  void OnTabModelRemoved(TabModel* tab_model) override;
+
+  // TabModelObserver:
+  void OnFinishingTabClosure(TabAndroid* tab,
+                             TabModel::TabClosingSource source) override;
+
+ private:
+  raw_ptr<Profile> profile_;
+  const raw_ptr<PageContentExtractionService> service_;
+
+  base::ScopedMultiSourceObservation<TabModel, TabModelObserver>
+      tab_model_observations_{this};
+};
+
+}  // namespace page_content_annotations
+
+#endif  // CHROME_BROWSER_PAGE_CONTENT_ANNOTATIONS_ANDROID_PAGE_CONTENT_EXTRACTION_TAB_MODEL_OBSERVER_ANDROID_H_
diff --git a/chrome/browser/page_content_annotations/annotate_page_content_request.cc b/chrome/browser/page_content_annotations/annotate_page_content_request.cc
index 2148c15..10b01385 100644
--- a/chrome/browser/page_content_annotations/annotate_page_content_request.cc
+++ b/chrome/browser/page_content_annotations/annotate_page_content_request.cc
@@ -4,10 +4,14 @@
 
 #include "chrome/browser/page_content_annotations/annotate_page_content_request.h"
 
+#include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/notimplemented.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "chrome/browser/page_content_annotations/page_content_cache_handler.h"
 #include "chrome/browser/page_content_annotations/page_content_extraction_service.h"
 #include "chrome/browser/page_content_annotations/page_content_extraction_service_factory.h"
 #include "chrome/browser/page_content_annotations/page_content_extraction_types.h"
@@ -19,12 +23,17 @@
 #include "components/pdf/common/constants.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/page.h"
 #include "net/http/http_response_headers.h"
 #include "pdf/buildflags.h"
 #include "services/metrics/public/cpp/metrics_utils.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 
+#if BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/android/tab_android.h"
+#endif
+
 #if BUILDFLAG(ENABLE_PDF)
 #include "components/pdf/browser/pdf_document_helper.h"
 #endif  // BUILDFLAG(ENABLE_PDF)
@@ -48,6 +57,18 @@
 }
 #endif  // BUILDFLAG(ENABLE_PDF)
 
+std::optional<int64_t> GetTabId(content::WebContents* web_contents) {
+#if BUILDFLAG(IS_ANDROID)
+  if (TabAndroid* tab = TabAndroid::FromWebContents(web_contents)) {
+    return tab->GetAndroidId();
+  }
+#else
+  // Implement an usable tab ID for other platforms.
+  NOTIMPLEMENTED();
+#endif
+  return std::nullopt;
+}
+
 }  // namespace
 
 // static
@@ -176,6 +197,15 @@
 
   // Drop pending extraction request for the previous page, if any.
   weak_factory_.InvalidateWeakPtrs();
+
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
+  auto* page_content_extraction_service =
+      PageContentExtractionServiceFactory::GetForProfile(profile);
+  if (page_content_extraction_service) {
+    page_content_extraction_service->OnNewNavigation(GetTabId(web_contents_),
+                                                     web_contents_);
+  }
 }
 
 void AnnotatedPageContentRequest::MaybeScheduleExtraction() {
@@ -185,15 +215,11 @@
 
   lifecycle_ = Lifecycle::kScheduled;
 
-  // Ignore the delay setting if the page is hidden. This would not affect user
-  // experience since page is not shown to users.
-  base::TimeDelta delay = is_hidden_ ? base::TimeDelta() : delay_;
-
   content::GetUIThreadTaskRunner()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&AnnotatedPageContentRequest::ExtractPageContent,
                      weak_factory_.GetWeakPtr()),
-      delay);
+      delay_);
 }
 
 void AnnotatedPageContentRequest::ExtractPageContent() {
@@ -288,7 +314,8 @@
   auto* page_content_extraction_service =
       PageContentExtractionServiceFactory::GetForProfile(profile);
   page_content_extraction_service->OnPageContentExtracted(
-      web_contents_->GetPrimaryPage(), page_content->proto);
+      web_contents_->GetPrimaryPage(), page_content->proto,
+      GetTabId(web_contents_));
 
   GURL url = web_contents_->GetLastCommittedURL();
   bool is_eligible_for_server_upload =
@@ -363,6 +390,15 @@
     return;
   }
 
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
+  auto* page_content_extraction_service =
+      PageContentExtractionServiceFactory::GetForProfile(profile);
+  if (page_content_extraction_service) {
+    page_content_extraction_service->OnVisibilityChanged(
+        GetTabId(web_contents_), web_contents_, visibility);
+  }
+
   auto triggering_mode = features::GetPageContentExtractionTriggeringMode();
   bool trigger_on_hide =
       triggering_mode ==
diff --git a/chrome/browser/page_content_annotations/annotate_page_content_request_unittest.cc b/chrome/browser/page_content_annotations/annotate_page_content_request_unittest.cc
index 02e79f01..ff2afe4f 100644
--- a/chrome/browser/page_content_annotations/annotate_page_content_request_unittest.cc
+++ b/chrome/browser/page_content_annotations/annotate_page_content_request_unittest.cc
@@ -28,13 +28,15 @@
  public:
   explicit TestPageContentExtractionService(
       content::BrowserContext* browser_context)
-      : PageContentExtractionService() {}
+      : PageContentExtractionService(/*os_crypt_async=*/nullptr,
+                                     browser_context->GetPath()) {}
   ~TestPageContentExtractionService() override = default;
 
   void OnPageContentExtracted(
       content::Page& page,
       const optimization_guide::proto::AnnotatedPageContent&
-          annotated_page_content) override {
+          annotated_page_content,
+      std::optional<int> tab_id) override {
     last_extracted_content_ = annotated_page_content;
     extraction_count_++;
     if (quit_closure_) {
diff --git a/chrome/browser/page_content_annotations/page_content_annotations_web_contents_observer.cc b/chrome/browser/page_content_annotations/page_content_annotations_web_contents_observer.cc
index cc4a25c..9ffbb920 100644
--- a/chrome/browser/page_content_annotations/page_content_annotations_web_contents_observer.cc
+++ b/chrome/browser/page_content_annotations/page_content_annotations_web_contents_observer.cc
@@ -127,6 +127,13 @@
   }
 }
 
+void PageContentAnnotationsWebContentsObserver::OnVisibilityChanged(
+    content::Visibility visibility) {
+  if (auto* annotated_page_content_request = GetAnnotatedPageContentRequest()) {
+    annotated_page_content_request->OnVisibilityChanged(visibility);
+  }
+}
+
 void PageContentAnnotationsWebContentsObserver::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   if (auto* annotated_page_content_request = GetAnnotatedPageContentRequest()) {
diff --git a/chrome/browser/page_content_annotations/page_content_annotations_web_contents_observer.h b/chrome/browser/page_content_annotations/page_content_annotations_web_contents_observer.h
index 023a69e..cdf2198fc 100644
--- a/chrome/browser/page_content_annotations/page_content_annotations_web_contents_observer.h
+++ b/chrome/browser/page_content_annotations/page_content_annotations_web_contents_observer.h
@@ -59,6 +59,7 @@
   void OnFirstContentfulPaintInPrimaryMainFrame() override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
+  void OnVisibilityChanged(content::Visibility visibility) override;
 
   // Invoked when related searches have been extracted for |visit|.
   void OnRelatedSearchesExtracted(
diff --git a/chrome/browser/page_content_annotations/page_content_cache_handler.cc b/chrome/browser/page_content_annotations/page_content_cache_handler.cc
new file mode 100644
index 0000000..a1b9af5
--- /dev/null
+++ b/chrome/browser/page_content_annotations/page_content_cache_handler.cc
@@ -0,0 +1,83 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_content_annotations/page_content_cache_handler.h"
+
+#include "components/os_crypt/async/browser/os_crypt_async.h"
+#include "components/page_content_annotations/core/page_content_cache.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/page.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace page_content_annotations {
+
+PageContentCacheHandler::PageContentCacheHandler(
+    os_crypt_async::OSCryptAsync* os_crypt_async,
+    const base::FilePath& profile_path)
+    : page_content_cache_(
+          std::make_unique<PageContentCache>(os_crypt_async, profile_path)) {}
+
+PageContentCacheHandler::~PageContentCacheHandler() = default;
+
+void PageContentCacheHandler::OnTabClosed(int64_t tab_id) {
+  page_content_cache_->RemovePageContentForTab(tab_id);
+}
+
+void PageContentCacheHandler::OnVisibilityChanged(
+    std::optional<int64_t> tab_id,
+    content::WebContents* web_contents,
+    content::Visibility visibility,
+    std::optional<optimization_guide::proto::AnnotatedPageContent> result) {
+  if (!tab_id || web_contents->GetBrowserContext()->IsOffTheRecord()) {
+    return;
+  }
+  if (visibility != content::Visibility::HIDDEN || !result) {
+    return;
+  }
+  // Even if background trigger is enabled, update the cache with available page
+  // contents. This is to avoid losing context if tab was killed as soon as it
+  // was hidden. If extraction succeeds, then cache would be updated again in
+  // ProcessPageContentExtraction().
+
+  // TODO(crbug.com/440643544): Pass in the extraction timestamp.
+  page_content_cache_->CachePageContent(
+      *tab_id, web_contents->GetLastCommittedURL(),
+      web_contents->GetController().GetLastCommittedEntry()->GetTimestamp(),
+      base::Time::Now(), *result);
+}
+
+void PageContentCacheHandler::OnNewNavigation(
+    std::optional<int64_t> tab_id,
+    content::WebContents* web_contents) {
+  if (!tab_id || web_contents->GetBrowserContext()->IsOffTheRecord()) {
+    return;
+  }
+  // Delete cached contents for the tab_id when page is updated.
+  page_content_cache_->RemovePageContentForTab(*tab_id);
+}
+
+void PageContentCacheHandler::ProcessPageContentExtraction(
+    std::optional<int64_t> tab_id,
+    content::WebContents* web_contents,
+    const optimization_guide::proto::AnnotatedPageContent& page_content) {
+  if (!tab_id || !web_contents ||
+      web_contents->GetBrowserContext()->IsOffTheRecord()) {
+    return;
+  }
+
+  // This method only handles the case when extraction finishes when tab is
+  // already backgrounded. We do not cache contents for active tab since it can
+  // be extracted on demand.
+  if (web_contents->GetVisibility() == content::Visibility::HIDDEN) {
+    page_content_cache_->CachePageContent(
+        *tab_id, web_contents->GetLastCommittedURL(),
+        web_contents->GetController().GetLastCommittedEntry()->GetTimestamp(),
+        base::Time::Now(), page_content);
+  }
+}
+
+}  // namespace page_content_annotations
diff --git a/chrome/browser/page_content_annotations/page_content_cache_handler.h b/chrome/browser/page_content_annotations/page_content_cache_handler.h
new file mode 100644
index 0000000..6824d6c8
--- /dev/null
+++ b/chrome/browser/page_content_annotations/page_content_cache_handler.h
@@ -0,0 +1,71 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PAGE_CONTENT_ANNOTATIONS_PAGE_CONTENT_CACHE_HANDLER_H_
+#define CHROME_BROWSER_PAGE_CONTENT_ANNOTATIONS_PAGE_CONTENT_CACHE_HANDLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <optional>
+
+#include "base/scoped_observation.h"
+#include "components/optimization_guide/proto/features/common_quality_data.pb.h"
+#include "content/public/browser/visibility.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace os_crypt_async {
+class OSCryptAsync;
+}  // namespace os_crypt_async
+
+namespace page_content_annotations {
+
+class PageContentCache;
+
+// Handles notifications from various observers to interact with the
+// PageContentCache.
+class PageContentCacheHandler {
+ public:
+  PageContentCacheHandler(os_crypt_async::OSCryptAsync* os_crypt_async,
+                          const base::FilePath& profile_path);
+  ~PageContentCacheHandler();
+
+  PageContentCacheHandler(const PageContentCacheHandler&) = delete;
+  PageContentCacheHandler& operator=(const PageContentCacheHandler&) = delete;
+
+  // Called when a tab is closed.
+  void OnTabClosed(int64_t tab_id);
+
+  // Called when the visibility of a WebContents changes.
+  void OnVisibilityChanged(
+      std::optional<int64_t> tab_id,
+      content::WebContents* web_contents,
+      content::Visibility visibility,
+      std::optional<optimization_guide::proto::AnnotatedPageContent> result);
+
+  // Called when a new navigation happens in a WebContents.
+  void OnNewNavigation(std::optional<int64_t> tab_id,
+                       content::WebContents* web_contents);
+
+  void ProcessPageContentExtraction(
+      std::optional<int64_t> tab_id,
+      content::WebContents* web_contents,
+      const optimization_guide::proto::AnnotatedPageContent& page_content);
+
+  PageContentCache* page_content_cache() { return page_content_cache_.get(); }
+
+ private:
+  std::unique_ptr<PageContentCache> page_content_cache_;
+};
+
+}  // namespace page_content_annotations
+
+#endif  // CHROME_BROWSER_PAGE_CONTENT_ANNOTATIONS_PAGE_CONTENT_CACHE_HANDLER_H_
diff --git a/chrome/browser/page_content_annotations/page_content_extraction_service.cc b/chrome/browser/page_content_annotations/page_content_extraction_service.cc
index cd29c0c..727f403 100644
--- a/chrome/browser/page_content_annotations/page_content_extraction_service.cc
+++ b/chrome/browser/page_content_annotations/page_content_extraction_service.cc
@@ -4,8 +4,11 @@
 
 #include "chrome/browser/page_content_annotations/page_content_extraction_service.h"
 
+#include "base/feature_list.h"
+#include "base/files/file_path.h"
 #include "chrome/browser/page_content_annotations/annotate_page_content_request.h"
 #include "chrome/browser/page_content_annotations/page_content_annotations_web_contents_observer.h"
+#include "chrome/browser/page_content_annotations/page_content_cache_handler.h"
 #include "chrome/browser/page_content_annotations/page_content_extraction_types.h"
 #include "components/optimization_guide/content/browser/page_content_proto_provider.h"
 #include "components/page_content_annotations/core/page_content_annotations_features.h"
@@ -13,9 +16,18 @@
 
 namespace page_content_annotations {
 
-PageContentExtractionService::PageContentExtractionService() = default;
+PageContentExtractionService::PageContentExtractionService(
+    os_crypt_async::OSCryptAsync* os_crypt_async,
+    const base::FilePath& profile_path) {
+  if (base::FeatureList::IsEnabled(features::kPageContentCache)) {
+    page_content_cache_handler_ =
+        std::make_unique<PageContentCacheHandler>(os_crypt_async, profile_path);
+  }
+}
 
-PageContentExtractionService::~PageContentExtractionService() = default;
+PageContentExtractionService::~PageContentExtractionService() {
+  ClearAllUserData();
+}
 
 void PageContentExtractionService::AddObserver(Observer* observer) {
   observers_.AddObserver(observer);
@@ -35,29 +47,78 @@
 
 void PageContentExtractionService::OnPageContentExtracted(
     content::Page& page,
-    const optimization_guide::proto::AnnotatedPageContent& page_content) {
+    const optimization_guide::proto::AnnotatedPageContent& page_content,
+    std::optional<int> tab_id) {
   for (auto& observer : observers_) {
     observer.OnPageContentExtracted(page, page_content);
   }
+
+  if (!page_content_cache_handler_) {
+    return;
+  }
+
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(&page.GetMainDocument());
+  if (!web_contents) {
+    return;
+  }
+
+  page_content_cache_handler_->ProcessPageContentExtraction(
+      tab_id, web_contents, page_content);
 }
 
 std::optional<ExtractedPageContentResult>
 PageContentExtractionService::GetExtractedPageContentAndEligibilityForPage(
     content::Page& page) {
-  // Get web contents for page.
-  content::WebContents* web_contents =
-      content::WebContents::FromRenderFrameHost(&page.GetMainDocument());
+  return GetCachedContentsFromWebContents(
+      content::WebContents::FromRenderFrameHost(&page.GetMainDocument()));
+}
+
+void PageContentExtractionService::OnTabClosed(int64_t tab_id) {
+  if (page_content_cache_handler_) {
+    page_content_cache_handler_->OnTabClosed(tab_id);
+  }
+}
+
+void PageContentExtractionService::OnVisibilityChanged(
+    std::optional<int64_t> tab_id,
+    content::WebContents* web_contents,
+    content::Visibility visibility) {
+  if (page_content_cache_handler_) {
+    std::optional<ExtractedPageContentResult> extracted_result =
+        GetCachedContentsFromWebContents(web_contents);
+    if (extracted_result) {
+      page_content_cache_handler_->OnVisibilityChanged(
+          tab_id, web_contents, visibility,
+          std::move(extracted_result->page_content));
+    }
+  }
+}
+
+void PageContentExtractionService::OnNewNavigation(
+    std::optional<int64_t> tab_id,
+    content::WebContents* web_contents) {
+  if (page_content_cache_handler_) {
+    page_content_cache_handler_->OnNewNavigation(tab_id, web_contents);
+  }
+}
+
+std::optional<ExtractedPageContentResult>
+PageContentExtractionService::GetCachedContentsFromWebContents(
+    content::WebContents* web_contents) {
+  if (!web_contents) {
+    return std::nullopt;
+  }
   PageContentAnnotationsWebContentsObserver* observer =
       PageContentAnnotationsWebContentsObserver::FromWebContents(web_contents);
-  if (!observer) {
-    return std::nullopt;
+  if (observer) {
+    AnnotatedPageContentRequest* request =
+        observer->GetAnnotatedPageContentRequest();
+    if (request) {
+      return request->GetCachedContentAndEligibility();
+    }
   }
-  AnnotatedPageContentRequest* request =
-      observer->GetAnnotatedPageContentRequest();
-  if (!request) {
-    return std::nullopt;
-  }
-  return request->GetCachedContentAndEligibility();
+  return std::nullopt;
 }
 
 }  // namespace page_content_annotations
diff --git a/chrome/browser/page_content_annotations/page_content_extraction_service.h b/chrome/browser/page_content_annotations/page_content_extraction_service.h
index 55d98ae..07af8ec 100644
--- a/chrome/browser/page_content_annotations/page_content_extraction_service.h
+++ b/chrome/browser/page_content_annotations/page_content_extraction_service.h
@@ -6,18 +6,31 @@
 #define CHROME_BROWSER_PAGE_CONTENT_ANNOTATIONS_PAGE_CONTENT_EXTRACTION_SERVICE_H_
 
 #include "base/observer_list.h"
+#include "base/supports_user_data.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/optimization_guide/proto/features/common_quality_data.pb.h"
+#include "content/public/browser/visibility.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
 
 namespace content {
 class Page;
+class WebContents;
 }  // namespace content
 
+namespace os_crypt_async {
+class OSCryptAsync;
+}  // namespace os_crypt_async
+
 namespace page_content_annotations {
 
 struct ExtractedPageContentResult;
+class PageContentCacheHandler;
 
-class PageContentExtractionService : public KeyedService {
+class PageContentExtractionService : public KeyedService,
+                                     public base::SupportsUserData {
  public:
   class Observer : public base::CheckedObserver {
    public:
@@ -28,7 +41,8 @@
         const optimization_guide::proto::AnnotatedPageContent& page_content) {}
   };
 
-  PageContentExtractionService();
+  PageContentExtractionService(os_crypt_async::OSCryptAsync* os_crypt_async,
+                               const base::FilePath& profile_path);
   ~PageContentExtractionService() override;
 
   void AddObserver(Observer* observer);
@@ -44,16 +58,34 @@
   std::optional<ExtractedPageContentResult>
   GetExtractedPageContentAndEligibilityForPage(content::Page& page);
 
+  // Called when a tab is closed.
+  void OnTabClosed(int64_t tab_id);
+
+  // Called when the visibility of a WebContents changes.
+  void OnVisibilityChanged(std::optional<int64_t> tab_id,
+                           content::WebContents* web_contents,
+                           content::Visibility visibility);
+
+  // Called when a new navigation happens in a WebContents.
+  void OnNewNavigation(std::optional<int64_t> tab_id,
+                       content::WebContents* web_contents);
+
  protected:
   friend class AnnotatedPageContentRequest;
 
   // Invoked when `page_content` is extracted for `page`, to notify the
-  // observers.
+  // observers. `tab_id` for the tab where page is loaded, if available.
   virtual void OnPageContentExtracted(
       content::Page& page,
-      const optimization_guide::proto::AnnotatedPageContent& page_content);
+      const optimization_guide::proto::AnnotatedPageContent& page_content,
+      std::optional<int> tab_id);
+
+  std::optional<ExtractedPageContentResult> GetCachedContentsFromWebContents(
+      content::WebContents* web_contents);
 
   base::ObserverList<Observer> observers_;
+
+  std::unique_ptr<PageContentCacheHandler> page_content_cache_handler_;
 };
 
 }  // namespace page_content_annotations
diff --git a/chrome/browser/page_content_annotations/page_content_extraction_service_factory.cc b/chrome/browser/page_content_annotations/page_content_extraction_service_factory.cc
index e2de40d..1b5e31a 100644
--- a/chrome/browser/page_content_annotations/page_content_extraction_service_factory.cc
+++ b/chrome/browser/page_content_annotations/page_content_extraction_service_factory.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/page_content_annotations/page_content_extraction_service_factory.h"
 
 #include "base/no_destructor.h"
+#include "build/build_config.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/page_content_annotations/page_content_extraction_service.h"
@@ -14,12 +15,23 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/page_content_annotations/core/page_content_annotations_features.h"
 
+#if BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/page_content_annotations/android/page_content_extraction_tab_model_observer_android.h"
+#endif
+
 #if BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #endif
 
 namespace page_content_annotations {
 
+namespace {
+#if BUILDFLAG(IS_ANDROID)
+const char kPageContentExtractionTabModelObserverAndroidKey[] =
+    "page_content_extraction_tab_model_observer_android";
+#endif
+}  // namespace
+
 // static
 PageContentExtractionService*
 PageContentExtractionServiceFactory::GetForProfile(Profile* profile) {
@@ -53,7 +65,20 @@
     return nullptr;
   }
 
-  return std::make_unique<PageContentExtractionService>();
+  Profile* profile = Profile::FromBrowserContext(context);
+  auto service = std::make_unique<PageContentExtractionService>(
+      g_browser_process->os_crypt_async(), profile->GetPath());
+
+#if BUILDFLAG(IS_ANDROID)
+  if (base::FeatureList::IsEnabled(features::kPageContentCache)) {
+    auto observer =
+        std::make_unique<PageContentExtractionTabModelObserverAndroid>(
+            profile, service.get());
+    service->SetUserData(kPageContentExtractionTabModelObserverAndroidKey,
+                         std::move(observer));
+  }
+#endif
+  return service;
 }
 
 bool PageContentExtractionServiceFactory::ServiceIsCreatedWithBrowserContext()
diff --git a/chrome/browser/pdf/pdf_extension_js_test.cc b/chrome/browser/pdf/pdf_extension_js_test.cc
index 6629381..a0adfc7 100644
--- a/chrome/browser/pdf/pdf_extension_js_test.cc
+++ b/chrome/browser/pdf/pdf_extension_js_test.cc
@@ -730,6 +730,51 @@
   // elements without loading a PDF is difficult.
   RunTestsInJsModule("save_to_drive_controls_test.js", "test.pdf");
 }
+
+class PDFExtensionJSSaveToDriveBeforeUnloadTest
+    : public PDFExtensionJSSaveToDriveTest {
+ protected:
+  // OOPIF PDF only, since MimeHandler handles the beforeunload event instead.
+  bool UseOopif() const override { return true; }
+};
+
+IN_PROC_BROWSER_TEST_P(PDFExtensionJSSaveToDriveBeforeUnloadTest, Uploading) {
+  RunTestsInJsModule("save_to_drive_before_unload_uploading_test.js",
+                     "test.pdf");
+}
+
+#if BUILDFLAG(ENABLE_PDF_INK2)
+class PDFExtensionJSInk2SaveToDriveBeforeUnloadTest
+    : public PDFExtensionJSSaveToDriveBeforeUnloadTest {
+ protected:
+  std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures()
+      const override {
+    auto enabled = PDFExtensionJSTest::GetEnabledFeatures();
+    enabled.push_back({chrome_pdf::features::kPdfInk2, {}});
+    enabled.push_back({chrome_pdf::features::kPdfSaveToDrive, {}});
+    return enabled;
+  }
+};
+
+IN_PROC_BROWSER_TEST_P(PDFExtensionJSInk2SaveToDriveBeforeUnloadTest,
+                       UploadCanceled) {
+  RunTestsInJsModule("ink2_save_to_drive_cancelled_before_unload_test.js",
+                     "test.pdf");
+}
+
+IN_PROC_BROWSER_TEST_P(PDFExtensionJSInk2SaveToDriveBeforeUnloadTest,
+                       UploadInProgressWithMoreEdits) {
+  RunTestsInJsModule(
+      "ink2_save_to_drive_uploading_more_edits_before_unload_test.js",
+      "test.pdf");
+}
+
+IN_PROC_BROWSER_TEST_P(PDFExtensionJSInk2SaveToDriveBeforeUnloadTest,
+                       UploadFinished) {
+  RunTestsInJsModule("ink2_save_to_drive_success_before_unload_test.js",
+                     "test.pdf");
+}
+#endif  // BUILDFLAG(ENABLE_PDF_INK2)
 #endif  // BUILDFLAG(ENABLE_PDF_SAVE_TO_DRIVE)
 
 // TODO(crbug.com/40268279): Stop testing both modes after OOPIF PDF viewer
@@ -749,4 +794,10 @@
 #endif  // BUILDFLAG(ENABLE_PDF_INK2)
 #if BUILDFLAG(ENABLE_PDF_SAVE_TO_DRIVE)
 INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(PDFExtensionJSSaveToDriveTest);
+INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(
+    PDFExtensionJSSaveToDriveBeforeUnloadTest);
+#if BUILDFLAG(ENABLE_PDF_INK2)
+INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(
+    PDFExtensionJSInk2SaveToDriveBeforeUnloadTest);
+#endif  // BUILDFLAG(ENABLE_PDF_INK2)
 #endif  // BUILDFLAG(ENABLE_PDF_SAVE_TO_DRIVE)
diff --git a/chrome/browser/performance_manager/policies/best_effort_task_inhibiting_policy.cc b/chrome/browser/performance_manager/policies/best_effort_task_inhibiting_policy.cc
index 106f967e..dd2f0f5a 100644
--- a/chrome/browser/performance_manager/policies/best_effort_task_inhibiting_policy.cc
+++ b/chrome/browser/performance_manager/policies/best_effort_task_inhibiting_policy.cc
@@ -22,7 +22,7 @@
 
 BestEffortTaskInhibitingPolicy::BestEffortTaskInhibitingPolicy() {
   // Validate values provided by feature params.
-  CHECK_GE(period_duration_, base::Seconds(30), base::NotFatalUntil::M145);
+  CHECK_GE(period_duration_, base::Seconds(1), base::NotFatalUntil::M145);
   CHECK_GT(period_duration_, minimum_duration_without_fence_,
            base::NotFatalUntil::M145);
 
diff --git a/chrome/browser/permissions/permission_revocation_request.cc b/chrome/browser/permissions/permission_revocation_request.cc
index 0e3e7bb..a1572221 100644
--- a/chrome/browser/permissions/permission_revocation_request.cc
+++ b/chrome/browser/permissions/permission_revocation_request.cc
@@ -99,6 +99,8 @@
     AbusiveNotificationPermissionsManager::
         ExecuteAbusiveNotificationAutoRevocation(
             HostContentSettingsMapFactory::GetForProfile(profile), origin,
+            safe_browsing::NotificationRevocationSource::
+                kManualSafeBrowsingRevocation,
             base::DefaultClock::GetInstance());
   } else {
     permissions::PermissionsClient::Get()
@@ -257,9 +259,10 @@
       NotifyCallback(Outcome::PERMISSION_REVOKED_DUE_TO_DISRUPTIVE_BEHAVIOR);
     }
 
-    base::UmaHistogramEnumeration("SafeBrowsing.NotificationRevocationSource",
-                                  safe_browsing::NotificationRevocationSource::
-                                      kManualSafeBrowsingRevocation);
+    safe_browsing::SafeBrowsingMetricsCollector::
+        LogSafeBrowsingNotificationRevocationSourceHistogram(
+            safe_browsing::NotificationRevocationSource::
+                kManualSafeBrowsingRevocation);
   } else {
     NotifyCallback(Outcome::PERMISSION_NOT_REVOKED);
   }
diff --git a/chrome/browser/permissions/permission_revocation_request_unittest.cc b/chrome/browser/permissions/permission_revocation_request_unittest.cc
index c08b40f..7f74219ad 100644
--- a/chrome/browser/permissions/permission_revocation_request_unittest.cc
+++ b/chrome/browser/permissions/permission_revocation_request_unittest.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/permissions/crowd_deny_preload_data.h"
 #include "chrome/browser/permissions/notifications_permission_revocation_config.h"
 #include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
+#include "chrome/browser/ui/safety_hub/abusive_notification_permissions_manager.h"
 #include "chrome/browser/ui/safety_hub/safety_hub_test_util.h"
 #include "chrome/browser/ui/safety_hub/safety_hub_util.h"
 #include "chrome/common/chrome_features.h"
@@ -585,4 +586,11 @@
   EXPECT_TRUE(safety_hub_util::IsUrlRevokedAbusiveNotification(
       HostContentSettingsMapFactory::GetForProfile(GetTestingProfile()),
       origin_to_revoke));
+  EXPECT_EQ(
+      safe_browsing::NotificationRevocationSource::
+          kManualSafeBrowsingRevocation,
+      AbusiveNotificationPermissionsManager::
+          GetRevokedAbusiveNotificationRevocationSource(
+              HostContentSettingsMapFactory::GetForProfile(GetTestingProfile()),
+              origin_to_revoke));
 }
diff --git a/chrome/browser/permissions/prediction_service/prediction_model_handler_provider.cc b/chrome/browser/permissions/prediction_service/prediction_model_handler_provider.cc
index 35f583fc..933b308 100644
--- a/chrome/browser/permissions/prediction_service/prediction_model_handler_provider.cc
+++ b/chrome/browser/permissions/prediction_service/prediction_model_handler_provider.cc
@@ -23,6 +23,31 @@
 
 namespace permissions {
 
+using optimization_guide::proto::OptimizationTarget;
+
+namespace {
+
+inline OptimizationTarget getGeolocationAiv4OptTarget() {
+#if BUILDFLAG(IS_ANDROID)
+  return OptimizationTarget::
+      OPTIMIZATION_TARGET_PERMISSIONS_AIV4_GEOLOCATION_ANDROID;
+#else
+  return OptimizationTarget::
+      OPTIMIZATION_TARGET_PERMISSIONS_AIV4_GEOLOCATION_DESKTOP;
+#endif
+}
+inline OptimizationTarget getNotificationsAiv4OptTarget() {
+#if BUILDFLAG(IS_ANDROID)
+  return OptimizationTarget::
+      OPTIMIZATION_TARGET_PERMISSIONS_AIV4_NOTIFICATIONS_ANDROID;
+#else
+  return OptimizationTarget::
+      OPTIMIZATION_TARGET_PERMISSIONS_AIV4_NOTIFICATIONS_DESKTOP;
+#endif
+}
+
+}  // namespace
+
 PredictionModelHandlerProvider::PredictionModelHandlerProvider(
     OptimizationGuideKeyedService* optimization_guide,
     passage_embeddings::EmbedderMetadataProvider* embedder_metadata_provider,
@@ -54,26 +79,22 @@
   notification_prediction_model_handler_ =
       std::make_unique<PredictionModelHandler>(
           optimization_guide,
-          optimization_guide::proto::OptimizationTarget::
+          OptimizationTarget::
               OPTIMIZATION_TARGET_NOTIFICATION_PERMISSION_PREDICTIONS);
 
   geolocation_prediction_model_handler_ =
       std::make_unique<PredictionModelHandler>(
           optimization_guide,
-          optimization_guide::proto::OptimizationTarget::
+          OptimizationTarget::
               OPTIMIZATION_TARGET_GEOLOCATION_PERMISSION_PREDICTIONS);
 
   if (IsAiv4ModelAvailable()) {
     VLOG(1) << "[PermissionsAI] PredictionModelHandlerProvider init AIv4";
     notification_aiv4_handler_ = std::make_unique<PermissionsAiv4Handler>(
-        optimization_guide,
-        optimization_guide::proto::OptimizationTarget::
-            OPTIMIZATION_TARGET_PERMISSIONS_AIV4_NOTIFICATIONS_DESKTOP,
+        optimization_guide, getNotificationsAiv4OptTarget(),
         RequestType::kNotifications);
     geolocation_aiv4_handler_ = std::make_unique<PermissionsAiv4Handler>(
-        optimization_guide,
-        optimization_guide::proto::OptimizationTarget::
-            OPTIMIZATION_TARGET_PERMISSIONS_AIV4_GEOLOCATION_DESKTOP,
+        optimization_guide, getGeolocationAiv4OptTarget(),
         RequestType::kGeolocation);
     return;
   }
@@ -81,12 +102,12 @@
     VLOG(1) << "[PermissionsAI] PredictionModelHandlerProvider init AIv3";
     notification_aiv3_handler_ = std::make_unique<PermissionsAiv3Handler>(
         optimization_guide,
-        optimization_guide::proto::OptimizationTarget::
+        OptimizationTarget::
             OPTIMIZATION_TARGET_NOTIFICATION_IMAGE_PERMISSION_RELEVANCE,
         RequestType::kNotifications);
     geolocation_aiv3_handler_ = std::make_unique<PermissionsAiv3Handler>(
         optimization_guide,
-        optimization_guide::proto::OptimizationTarget::
+        OptimizationTarget::
             OPTIMIZATION_TARGET_GEOLOCATION_IMAGE_PERMISSION_RELEVANCE,
         RequestType::kGeolocation);
     return;
diff --git a/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper.h b/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper.h
index 91d469f7..58029a62 100644
--- a/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper.h
+++ b/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper.h
@@ -9,6 +9,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/clock.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/browser/picture_in_picture/auto_picture_in_picture_safe_browsing_checker_client.h"
 #include "chrome/browser/picture_in_picture/auto_pip_setting_helper.h"
 #include "components/content_settings/core/common/content_settings.h"
@@ -19,7 +20,10 @@
 #include "services/media_session/public/mojom/audio_focus.mojom.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
+
+#if !BUILDFLAG(IS_ANDROID)
 #include "ui/views/bubble/bubble_border.h"
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 namespace permissions {
 class PermissionDecisionAutoBlockerBase;
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc
index 28b9c978..de8cfaa 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc
@@ -7,7 +7,6 @@
 #include "base/memory/raw_ptr.h"
 #include "base/numerics/safe_conversions.h"
 #include "chrome/browser/picture_in_picture/picture_in_picture_bounds_cache.h"
-#include "chrome/browser/picture_in_picture/picture_in_picture_occlusion_tracker.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "content/public/browser/document_picture_in_picture_window_controller.h"
 #include "content/public/browser/navigation_handle.h"
@@ -34,6 +33,7 @@
 // Android when supporting document PiP.
 #include "chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper.h"
 #include "chrome/browser/picture_in_picture/auto_pip_setting_overlay_view.h"
+#include "chrome/browser/picture_in_picture/picture_in_picture_occlusion_tracker.h"
 #include "chrome/browser/picture_in_picture/picture_in_picture_window.h"
 #include "media/base/media_switches.h"
 #include "net/base/url_util.h"
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h
index c75dda7c..2b1b021 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h
@@ -16,12 +16,12 @@
 #include "third_party/blink/public/mojom/picture_in_picture_window_options/picture_in_picture_window_options.mojom.h"
 #include "ui/display/display.h"
 #include "ui/gfx/geometry/rect.h"
-#include "ui/views/bubble/bubble_border.h"
 #include "url/gurl.h"
 
 #if !BUILDFLAG(IS_ANDROID)
 #include "base/types/pass_key.h"
 #include "chrome/browser/picture_in_picture/picture_in_picture_window_manager_uma_helper.h"
+#include "ui/views/bubble/bubble_border.h"
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 namespace content {
diff --git a/chrome/browser/platform_experience/installer/installer_win.cc b/chrome/browser/platform_experience/installer/installer_win.cc
index 867067d..e9811a5 100644
--- a/chrome/browser/platform_experience/installer/installer_win.cc
+++ b/chrome/browser/platform_experience/installer/installer_win.cc
@@ -12,6 +12,7 @@
 #include "base/path_service.h"
 #include "base/process/launch.h"
 #include "base/win/scoped_variant.h"
+#include "base/win/windows_version.h"
 #include "chrome/browser/google/google_update_app_command.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/install_static/install_details.h"
@@ -45,6 +46,15 @@
   return base::PathExists(peh_exe_path);
 }
 
+// This function might block.
+// Returns whether we should attempt installing the platform experience helper.
+bool ShouldInstallPlatformExperienceHelper() {
+  if (base::win::GetVersion() < base::win::Version::WIN10) {
+    return false;
+  }
+  return !PlatformExperienceHelperMightBeInstalled();
+}
+
 // Enum for tracking the launch status of the platform experience helper
 // installer for system installs.
 // These values are persisted to logs. Entries should not be renumbered and
@@ -119,7 +129,7 @@
 }
 
 void MaybeInstallPlatformExperienceHelper() {
-  if (PlatformExperienceHelperMightBeInstalled()) {
+  if (!ShouldInstallPlatformExperienceHelper()) {
     return;
   }
 
diff --git a/chrome/browser/platform_experience/win b/chrome/browser/platform_experience/win
index 79f30db..8687ef93 160000
--- a/chrome/browser/platform_experience/win
+++ b/chrome/browser/platform_experience/win
@@ -1 +1 @@
-Subproject commit 79f30db1b0f936ed6647c2cf386d0517582cb8a0
+Subproject commit 8687ef93f5137bac130198b3c0c39f5a4e83234b
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_browsertest.cc b/chrome/browser/policy/cloud/user_policy_signin_service_browsertest.cc
index 5b84219..ac267ee 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service_browsertest.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service_browsertest.cc
@@ -92,6 +92,8 @@
 
 }  // namespace
 
+// TODO(https://crbug.com/340623286): Convert this test suite to use the
+// `ProfileManagementDisclaimerService`.
 class UserPolicySigninServiceTest : public InProcessBrowserTest {
  public:
   UserPolicySigninServiceTest()
@@ -307,7 +309,6 @@
     return http_response;
   }
 
-  base::test::ScopedFeatureList feature_list_;
   net::EmbeddedTestServer embedded_test_server_;
   FakeGaia fake_gaia_;
   std::unique_ptr<policy::EmbeddedPolicyTestServer> policy_server_;
@@ -368,7 +369,8 @@
   NOTREACHED();
 }
 
-// Disabled for Win11 arm64 flakes: https://crbug.com/340623286
+// TODO(https://crbug.com/340623286): Convert this test to use the
+// `ProfileManagementDisclaimerService`.
 IN_PROC_BROWSER_TEST_F(UserPolicySigninServiceTest, DISABLED_BasicSignin) {
   EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kShowHomeButton));
   EXPECT_TRUE(signin_client()->IsClearPrimaryAccountAllowed());
@@ -393,7 +395,8 @@
   EXPECT_TRUE(signin_client()->IsRevokeSyncConsentAllowed());
 }
 
-// Disabled for Win11 arm64 flakes: https://crbug.com/340623286
+// TODO(https://crbug.com/340623286): Convert this test to use the
+// `ProfileManagementDisclaimerService`.
 IN_PROC_BROWSER_TEST_F(UserPolicySigninServiceTest, DISABLED_UndoSignin) {
   EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kShowHomeButton));
   EXPECT_FALSE(enterprise_util::UserAcceptedAccountManagement(profile()));
@@ -423,7 +426,8 @@
   EXPECT_TRUE(enterprise_util::ProfileCanBeManaged(profile()));
 }
 
-// Disabled for Win11 arm64 flakes: https://crbug.com/340623286
+// TODO(https://crbug.com/340623286): Convert this test to use the
+// `ProfileManagementDisclaimerService`.
 IN_PROC_BROWSER_TEST_F(UserPolicySigninServiceTest,
                        DISABLED_AcceptManagementDeclineSync) {
   TurnSyncOnHelper::SetShowSyncEnabledUiForTesting(true);
@@ -460,6 +464,8 @@
   TurnSyncOnHelper::SetShowSyncEnabledUiForTesting(false);
 }
 
+// This test can be deleted once syncer::kReplaceSyncPromosWithSignInPromos is
+// launched.
 class UserPolicySigninServiceTestWithReplaceSyncPromosWithSignInPromosDisabled
     : public UserPolicySigninServiceTest {
  public:
diff --git a/chrome/browser/policy/system_features_disable_list_policy_handler.cc b/chrome/browser/policy/system_features_disable_list_policy_handler.cc
index 93edf4b..49536e3 100644
--- a/chrome/browser/policy/system_features_disable_list_policy_handler.cc
+++ b/chrome/browser/policy/system_features_disable_list_policy_handler.cc
@@ -44,6 +44,7 @@
 const char kGoogleMapsFeature[] = "google_maps";
 const char kCalculatorFeature[] = "calculator";
 const char kTextEditorFeature[] = "text_editor";
+const char kVidsFeature[] = "vids";
 
 const char kSystemFeaturesDisableListHistogram[] =
     "Enterprise.SystemFeaturesDisableList";
@@ -179,6 +180,9 @@
   if (system_feature == kTextEditorFeature) {
     return SystemFeature::kTextEditor;
   }
+  if (system_feature == kVidsFeature) {
+    return SystemFeature::kVids;
+  }
   LOG(ERROR) << "Unsupported system feature: " << system_feature;
   return SystemFeature::kUnknownSystemFeature;
 }
diff --git a/chrome/browser/policy/system_features_disable_list_policy_handler.h b/chrome/browser/policy/system_features_disable_list_policy_handler.h
index b9792f1..51a5946 100644
--- a/chrome/browser/policy/system_features_disable_list_policy_handler.h
+++ b/chrome/browser/policy/system_features_disable_list_policy_handler.h
@@ -46,7 +46,8 @@
   kGoogleMaps = 24,           // The Google Maps app on ChromeOS.
   kCalculator = 25,           // The Calculator app on ChromeOS.
   kTextEditor = 26,           // The Text Editor app on ChromeOS.
-  kMaxValue = kTextEditor,
+  kVids = 27,                 // The Vids app on ChromeOS.
+  kMaxValue = kVids,
 };
 
 // A disabling mode that decides the user experience when a system feature is
@@ -83,6 +84,7 @@
 extern const char kGoogleMapsFeature[];
 extern const char kCalculatorFeature[];
 extern const char kTextEditorFeature[];
+extern const char kVidsFeature[];
 
 extern const char kBlockedDisableMode[];
 extern const char kHiddenDisableMode[];
diff --git a/chrome/browser/policy/system_features_disable_list_policy_handler_unittest.cc b/chrome/browser/policy/system_features_disable_list_policy_handler_unittest.cc
index 4af70f7..560d1d74 100644
--- a/chrome/browser/policy/system_features_disable_list_policy_handler_unittest.cc
+++ b/chrome/browser/policy/system_features_disable_list_policy_handler_unittest.cc
@@ -77,7 +77,7 @@
        "recorder",      "gmail",        "google_docs",      "google_slides",
        "google_sheets", "google_drive", "google_keep",      "google_calendar",
        "google_chat",   "youtube",      "google_maps",      "calculator",
-       "text_editor"});
+       "text_editor",   "vids"});
 
   VerifyPrefList(
       {SystemFeature::kCamera,          SystemFeature::kOsSettings,
@@ -92,7 +92,7 @@
        SystemFeature::kGoogleKeep,      SystemFeature::kGoogleCalendar,
        SystemFeature::kGoogleChat,      SystemFeature::kYoutube,
        SystemFeature::kGoogleMaps,      SystemFeature::kCalculator,
-       SystemFeature::kTextEditor});
+       SystemFeature::kTextEditor,      SystemFeature::kVids});
 
   std::vector<base::Bucket> expected_histogram{
       base::Bucket(static_cast<int>(SystemFeature::kCamera), 1),
@@ -119,7 +119,8 @@
       base::Bucket(static_cast<int>(SystemFeature::kYoutube), 1),
       base::Bucket(static_cast<int>(SystemFeature::kGoogleMaps), 1),
       base::Bucket(static_cast<int>(SystemFeature::kCalculator), 1),
-      base::Bucket(static_cast<int>(SystemFeature::kTextEditor), 1)};
+      base::Bucket(static_cast<int>(SystemFeature::kTextEditor), 1),
+      base::Bucket(static_cast<int>(SystemFeature::kVids), 1)};
 
   EXPECT_EQ(
       histogram_tester_.GetAllSamples(kSystemFeaturesDisableListHistogram),
diff --git a/chrome/browser/preferences/BUILD.gn b/chrome/browser/preferences/BUILD.gn
index 7cb8743..1bcdd78 100644
--- a/chrome/browser/preferences/BUILD.gn
+++ b/chrome/browser/preferences/BUILD.gn
@@ -70,6 +70,7 @@
     "//components/privacy_sandbox/privacy_sandbox_prefs.h",
     "//components/privacy_sandbox/tracking_protection_prefs.h",
     "//components/safe_browsing/core/common/safe_browsing_prefs.h",
+    "//components/safety_check/safety_check_pref_names.h",
     "//components/saved_tab_groups/public/pref_names.h",
     "//components/signin/public/base/signin_pref_names.cc",
     "//components/supervised_user/core/common/pref_names.h",
diff --git a/chrome/browser/prefs/android/java/src/org/chromium/chrome/browser/prefs/LocalStatePrefs.java b/chrome/browser/prefs/android/java/src/org/chromium/chrome/browser/prefs/LocalStatePrefs.java
index 40e0d40..5434b1b 100644
--- a/chrome/browser/prefs/android/java/src/org/chromium/chrome/browser/prefs/LocalStatePrefs.java
+++ b/chrome/browser/prefs/android/java/src/org/chromium/chrome/browser/prefs/LocalStatePrefs.java
@@ -4,21 +4,36 @@
 
 package org.chromium.chrome.browser.prefs;
 
+import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.components.prefs.PrefService;
 
 /** Helper for retrieving the Local State {@link PrefService}. */
 @JNINamespace("chrome_browser_prefs")
 @NullMarked
 public class LocalStatePrefs {
+    private static boolean sIsNativeReady;
+
     /** Returns the {@link PrefService} associated with local state. */
-    public static PrefService get() {
+    public static @Nullable PrefService get() {
+        if (!areNativePrefsLoaded()) return null;
         return LocalStatePrefsJni.get().getPrefService();
     }
 
+    /** Returns whether the native side is initialized. */
+    public static boolean areNativePrefsLoaded() {
+        return sIsNativeReady;
+    }
+
+    @CalledByNative
+    private static void setNativePrefsLoaded() {
+        sIsNativeReady = true;
+    }
+
     @NativeMethods
     public interface Natives {
         PrefService getPrefService();
diff --git a/chrome/browser/prefs/android/local_state_prefs_android.cc b/chrome/browser/prefs/android/local_state_prefs_android.cc
index 5d2bfc7..e59d026 100644
--- a/chrome/browser/prefs/android/local_state_prefs_android.cc
+++ b/chrome/browser/prefs/android/local_state_prefs_android.cc
@@ -11,6 +11,11 @@
 
 namespace chrome_browser_prefs {
 
+void OnLocalStatePrefsLoaded() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_LocalStatePrefs_setNativePrefsLoaded(env);
+}
+
 static base::android::ScopedJavaLocalRef<jobject>
 JNI_LocalStatePrefs_GetPrefService(JNIEnv* env) {
   return g_browser_process->local_state()->GetJavaObject();
diff --git a/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline.cc b/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline.cc
index a23c2f0..a716945 100644
--- a/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline.cc
+++ b/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline.cc
@@ -69,7 +69,12 @@
       std::move(same_url_matcher),
       web_contents.GetPrimaryMainFrame()->GetPageUkmSourceId());
 
-  if (IsSearchUrl(web_contents, url_)) {
+  bool is_search_url = IsSearchUrl(web_contents, url_);
+  base::UmaHistogramBoolean(
+      "Navigation.Prefetch.IsPrefetchingSRPUrl.Embedder_BookmarkBar",
+      is_search_url);
+
+  if (is_search_url) {
     attempt->SetEligibility(ToPreloadingEligibility(
         ChromePreloadingEligibility::KDisallowSearchUrl));
     return;
diff --git a/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_unittest.cc b/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_unittest.cc
index 6e6bdc6e..0447f17 100644
--- a/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_unittest.cc
+++ b/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_unittest.cc
@@ -82,7 +82,7 @@
 
 // Test that a search related url is ignored by the prerender BookmarkBar
 // trigger.
-TEST_F(BookmarkBarPreloadPipelineManagerTest, DisallowSearchUrlBookmarkBar) {
+TEST_F(BookmarkBarPreloadPipelineManagerTest, PrerenderDisallowSearchUrl) {
   base::HistogramTester histogram_tester;
   GURL prerendering_url = GetSearchSuggestionUrl("prer", "prerender");
   bookmarkbar_preload_manager()->StartPrerender(prerendering_url);
@@ -92,4 +92,15 @@
       "Prerender.IsPrerenderingSRPUrl.Embedder_BookmarkBar", true, 1);
 }
 
+// Test that a search related url is ignored by the prefetch BookmarkBar
+// trigger.
+TEST_F(BookmarkBarPreloadPipelineManagerTest, PrefetchDisallowSearchUrl) {
+  base::HistogramTester histogram_tester;
+  GURL prefetch_url = GetSearchSuggestionUrl("pref", "prefetch");
+  bookmarkbar_preload_manager()->StartPrefetch(prefetch_url);
+
+  histogram_tester.ExpectUniqueSample(
+      "Navigation.Prefetch.IsPrefetchingSRPUrl.Embedder_BookmarkBar", true, 1);
+}
+
 }  // namespace
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/cache_alias_search_prefetch_url_loader.cc b/chrome/browser/preloading/prefetch/search_prefetch/cache_alias_search_prefetch_url_loader.cc
index 9365a83..a9db28de 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/cache_alias_search_prefetch_url_loader.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/cache_alias_search_prefetch_url_loader.cc
@@ -26,9 +26,19 @@
 
 CacheAliasSearchPrefetchURLLoader::CacheAliasSearchPrefetchURLLoader(
     Profile* profile,
+    const net::NetworkTrafficAnnotationTag& network_traffic_annotation)
+    : CacheAliasSearchPrefetchURLLoader(profile,
+                                        network_traffic_annotation,
+                                        /*prefetch_url=*/GURL(),
+                                        Mode::kDryRun) {}
+
+CacheAliasSearchPrefetchURLLoader::CacheAliasSearchPrefetchURLLoader(
+    Profile* profile,
     const net::NetworkTrafficAnnotationTag& network_traffic_annotation,
-    const GURL& prefetch_url)
-    : url_loader_factory_(profile->GetDefaultStoragePartition()
+    const GURL& prefetch_url,
+    Mode mode)
+    : mode_(mode),
+      url_loader_factory_(profile->GetDefaultStoragePartition()
                               ->GetURLLoaderFactoryForBrowserProcess()),
       search_prefetch_service_(
           SearchPrefetchServiceFactory::GetForProfile(profile)->GetWeakPtr()),
@@ -80,7 +90,13 @@
   network::ResourceRequest prefetch_request = *resource_request_;
 
   prefetch_request.load_flags |= net::LOAD_ONLY_FROM_CACHE;
-  prefetch_request.url = prefetch_url_;
+  switch (mode_) {
+    case Mode::kNormal:
+      prefetch_request.url = prefetch_url_;
+      break;
+    case Mode::kDryRun:
+      break;
+  }
 
   // Create a network service URL loader with passed in params.
   url_loader_factory_->CreateLoaderAndStart(
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/cache_alias_search_prefetch_url_loader.h b/chrome/browser/preloading/prefetch/search_prefetch/cache_alias_search_prefetch_url_loader.h
index 9ca982f9..a6a1174 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/cache_alias_search_prefetch_url_loader.h
+++ b/chrome/browser/preloading/prefetch/search_prefetch/cache_alias_search_prefetch_url_loader.h
@@ -48,11 +48,33 @@
   };
   // LINT.ThenChange(//tools/metrics/histograms/metadata/omnibox/enums.xml:SearchPrefetchCacheAliasFallbackReasonEnum)
 
+  // For investigating the cache miss cases, tentatively introduce a DryRun
+  // mode.
+  // TODO(https://crbug.com/413557424): Remove the DryRun mode once the
+  // investigation is done.
+  enum class Mode {
+    // Uses a stored url to help the resource request to locate the
+    // corresponding prefetched response.
+    kNormal = 0,
+    // Request's URL remains the same. It entirely relies on HttpCache to locate
+    // the prefetched response. Ideally the two modes should have the same
+    // success rate, thanks to NoVarySearch disk cache support.
+    kDryRun = 1,
+  };
+
+  // Creates a DryRun mode CacheAliasSearchPrefetchURLLoader for debugging.
+  // TODO(https://crbug.com/413557424): Remove the DryRun mode once the
+  // investigation is done.
+  CacheAliasSearchPrefetchURLLoader(
+      Profile* profile,
+      const net::NetworkTrafficAnnotationTag& network_traffic_annotation);
+
   // Creates and stores state needed to do the cache lookup.
   CacheAliasSearchPrefetchURLLoader(
       Profile* profile,
       const net::NetworkTrafficAnnotationTag& network_traffic_annotation,
-      const GURL& prefetch_url);
+      const GURL& prefetch_url,
+      Mode mode = Mode::kNormal);
 
   ~CacheAliasSearchPrefetchURLLoader() override;
 
@@ -110,6 +132,8 @@
   // Starts the cache only request to |prefetch_url_|.
   void StartLoadCachedPrefetchResponse();
 
+  const Mode mode_;
+
   // The network URLLoader that fetches the prefetch URL and its receiver.
   mojo::Remote<network::mojom::URLLoader> network_url_loader_;
   mojo::Receiver<network::mojom::URLLoaderClient> url_loader_receiver_{this};
@@ -127,7 +151,8 @@
 
   net::NetworkTrafficAnnotationTag network_traffic_annotation_;
 
-  // The URL for the prefetch response stored in cache.
+  // The URL for the prefetch response stored in cache. Will be an invalid URL
+  // when `this` is running in the DryRun mode.
   const GURL prefetch_url_;
 
   // Set when RestartDirect() is called.
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/field_trial_settings.cc b/chrome/browser/preloading/prefetch/search_prefetch/field_trial_settings.cc
index 4449a1e..8807671 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/field_trial_settings.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/field_trial_settings.cc
@@ -21,6 +21,11 @@
 BASE_FEATURE(kSearchPrefetchWithNoVarySearchDiskCache,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Kill-switch for debugging CacheAliasLoader and NVS's cache hit rate.
+// TODO(https://crbug.com/413557424): Remove the DryRun mode once the
+// investigation is done.
+BASE_FEATURE(kCacheAliasLoaderDryRunMode, base::FEATURE_ENABLED_BY_DEFAULT);
+
 bool SearchPrefetchServicePrefetchingIsEnabled() {
   if (!base::FeatureList::IsEnabled(kSearchPrefetchServicePrefetching)) {
     return false;
@@ -116,6 +121,10 @@
          base::FeatureList::IsEnabled(kSearchPrefetchWithNoVarySearchDiskCache);
 }
 
+bool CacheAliasLoaderDryRunModeEnabled() {
+  return base::FeatureList::IsEnabled(kCacheAliasLoaderDryRunMode);
+}
+
 bool IsPrefetchIncognitoEnabled() {
   return SearchPrefetchServicePrefetchingIsEnabled() &&
          IsSearchNavigationPrefetchEnabled() &&
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/field_trial_settings.h b/chrome/browser/preloading/prefetch/search_prefetch/field_trial_settings.h
index 9077ffe1..089f550 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/field_trial_settings.h
+++ b/chrome/browser/preloading/prefetch/search_prefetch/field_trial_settings.h
@@ -73,6 +73,9 @@
 // being the default match.
 bool OnlyAllowDefaultMatchPreloading();
 bool IsNoVarySearchDiskCacheEnabled();
+// TODO(https://crbug.com/413557424): Remove the DryRun mode once the
+// investigation is done.
+bool CacheAliasLoaderDryRunModeEnabled();
 
 // Allows the omnibox search prefetch in Incognito.
 //
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
index 6c6646e..22f73ed 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
@@ -628,13 +628,23 @@
 SearchPrefetchURLLoader::RequestHandler
 SearchPrefetchService::TakePrefetchResponseFromDiskCache(
     const GURL& navigation_url) {
-  DCHECK(!IsNoVarySearchDiskCacheEnabled());
+  CHECK(!IsNoVarySearchDiskCacheEnabled() ||
+        CacheAliasLoaderDryRunModeEnabled());
   GURL navigation_url_without_ref(net::SimplifyUrlForRequest(navigation_url));
   if (prefetch_cache_.find(navigation_url_without_ref) ==
       prefetch_cache_.end()) {
     return {};
   }
 
+  if (IsNoVarySearchDiskCacheEnabled()) {
+    if (!CacheAliasLoaderDryRunModeEnabled()) {
+      return {};
+    }
+    auto loader = std::make_unique<CacheAliasSearchPrefetchURLLoader>(
+        profile_, SearchPrefetchRequest::NetworkAnnotationForPrefetch());
+    return CacheAliasSearchPrefetchURLLoader::
+        GetServingResponseHandlerFromLoader(std::move(loader));
+  }
   auto loader = std::make_unique<CacheAliasSearchPrefetchURLLoader>(
       profile_, SearchPrefetchRequest::NetworkAnnotationForPrefetch(),
       prefetch_cache_[navigation_url_without_ref].first);
@@ -898,9 +908,13 @@
 }
 
 void SearchPrefetchService::ClearCacheEntry(const GURL& navigation_url) {
-  if (IsNoVarySearchDiskCacheEnabled()) {
+  // Only update the profile data when disk cache is disabled or dry run mode is
+  // enabled.
+  if (IsNoVarySearchDiskCacheEnabled() &&
+      !CacheAliasLoaderDryRunModeEnabled()) {
     return;
   }
+
   GURL navigation_url_without_ref(net::SimplifyUrlForRequest(navigation_url));
   if (prefetch_cache_.find(navigation_url_without_ref) ==
       prefetch_cache_.end()) {
@@ -912,7 +926,6 @@
 }
 
 void SearchPrefetchService::UpdateServeTime(const GURL& navigation_url) {
-  DCHECK(!IsNoVarySearchDiskCacheEnabled());
   GURL navigation_url_without_ref(net::SimplifyUrlForRequest(navigation_url));
   if (prefetch_cache_.find(navigation_url_without_ref) == prefetch_cache_.end())
     return;
@@ -923,11 +936,15 @@
 
 void SearchPrefetchService::AddCacheEntry(const GURL& navigation_url,
                                           const GURL& prefetch_url) {
-  // Disk cache is responsible for retrieving the cache and we do not need to
-  // modify the URL to help the disk cache retrieve the cache.
-  if (IsNoVarySearchDiskCacheEnabled()) {
+  // Only update the profile data when disk cache is disabled or dry run mode is
+  // enabled.
+  if (IsNoVarySearchDiskCacheEnabled() &&
+      !CacheAliasLoaderDryRunModeEnabled()) {
     return;
   }
+
+  // Disk cache is responsible for retrieving the cache and we do not need to
+  // modify the URL to help the disk cache retrieve the cache.
   GURL navigation_url_without_ref(net::SimplifyUrlForRequest(navigation_url));
   GURL prefetch_url_without_ref(net::SimplifyUrlForRequest(prefetch_url));
   if (navigation_url_without_ref == prefetch_url_without_ref) {
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
index 54c989c..6b258aa1 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
@@ -1504,13 +1504,9 @@
   EXPECT_TRUE(base::Contains(inner_html, "prefetch"));
   histogram_tester.ExpectTotalCount(
       "Omnibox.SearchPrefetch.ClickToNavigationIntercepted", 2);
-  // When NoVarySearchDiskCache is enabled, the request is not handled by
-  // CacheAliasSearchPrefetchURLLoader, so it should not record anything
-  // new.
-  const int expected_count = IsNoVarySearchDiskCacheEnabled() ? 2 : 3;
+
   histogram_tester.ExpectTotalCount(
-      "Omnibox.SearchPrefetch.NavigationInterceptedToForwardingComplete",
-      expected_count);
+      "Omnibox.SearchPrefetch.NavigationInterceptedToForwardingComplete", 3);
   histogram_tester.ExpectTotalCount(
       "Omnibox.SearchPrefetch.DuplicateSearchTermsAge", 0);
 }
@@ -1607,13 +1603,9 @@
   EXPECT_TRUE(base::Contains(inner_html, "prefetch"));
   histogram_tester.ExpectTotalCount(
       "Omnibox.SearchPrefetch.ClickToNavigationIntercepted", 2);
-  // When NoVarySearchDiskCache is enabled, the request is not handled by
-  // CacheAliasSearchPrefetchURLLoader, so it should not record anything
-  // new.
-  const int expected_count = IsNoVarySearchDiskCacheEnabled() ? 2 : 3;
+
   histogram_tester.ExpectTotalCount(
-      "Omnibox.SearchPrefetch.NavigationInterceptedToForwardingComplete",
-      expected_count);
+      "Omnibox.SearchPrefetch.NavigationInterceptedToForwardingComplete", 3);
 }
 
 IN_PROC_BROWSER_TEST_P(SearchPrefetchServiceEnabledWithNVSBrowserTest,
@@ -1803,10 +1795,6 @@
   EXPECT_TRUE(base::Contains(inner_html, "regular"));
   EXPECT_FALSE(base::Contains(inner_html, "prefetch"));
 
-  // If NVS is enabled, we do not rely on CacheAlias hack.
-  if (IsNoVarySearchDiskCacheEnabled()) {
-    return;
-  }
   histogram_tester.ExpectUniqueSample(
       "Omnibox.SearchPrefetch.CacheAliasFallbackReason",
       CacheAliasSearchPrefetchURLLoader::FallbackReason::kErrorOnComplete, 1);
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_url_loader_interceptor.cc b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_url_loader_interceptor.cc
index d74787fe..0dbc58d 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_url_loader_interceptor.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_url_loader_interceptor.cc
@@ -128,7 +128,8 @@
   if (handler) {
     return handler;
   }
-  if (IsNoVarySearchDiskCacheEnabled()) {
+  if (IsNoVarySearchDiskCacheEnabled() &&
+      !CacheAliasLoaderDryRunModeEnabled()) {
     return {};
   }
   if (tentative_resource_request.load_flags & net::LOAD_SKIP_CACHE_VALIDATION) {
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_preload_unified_browsertest.cc b/chrome/browser/preloading/prefetch/search_prefetch/search_preload_unified_browsertest.cc
index 2aeb909..805f52d 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_preload_unified_browsertest.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_preload_unified_browsertest.cc
@@ -1302,15 +1302,11 @@
   EXPECT_EQ(0, prerender_helper().GetRequestCount(expected_prerender_url_1));
   EXPECT_EQ(1, prerender_helper().GetRequestCount(expected_prefetch_url_1));
 
-  if (!GetParam()) {
-    // If NoVarySearch is enabled, we do not use
-    // CacheAliasSearchPrefetchURLLoader so it does not record anything.
-    histogram_tester.ExpectUniqueSample(
-        "Omnibox.SearchPrefetch.CacheAliasFallbackReason",
-        CacheAliasSearchPrefetchURLLoader::FallbackReason::kNoFallback, 1);
-    histogram_tester.ExpectTotalCount(
-        "Omnibox.SearchPrefetch.CacheAliasElapsedTimeToFallback", 0);
-  }
+  histogram_tester.ExpectUniqueSample(
+      "Omnibox.SearchPrefetch.CacheAliasFallbackReason",
+      CacheAliasSearchPrefetchURLLoader::FallbackReason::kNoFallback, 1);
+  histogram_tester.ExpectTotalCount(
+      "Omnibox.SearchPrefetch.CacheAliasElapsedTimeToFallback", 0);
 }
 
 // Tests the started prerender is destroyed after prefetch request expired.
diff --git a/chrome/browser/privacy_guide/README.md b/chrome/browser/privacy_guide/README.md
new file mode 100644
index 0000000..11587e5
--- /dev/null
+++ b/chrome/browser/privacy_guide/README.md
@@ -0,0 +1,22 @@
+# Privacy Guide
+
+This directory contains the Privacy Guide feature.
+
+The Privacy Guide is a step-by-step guide, accessible from the Privacy and
+Security section of Chrome's settings, to help users understand and manage
+their privacy settings. The main goal of the Privacy Guide is to make key
+privacy controls more accessible and understandable for everyday users.
+
+## Key Components
+
+The Privacy Guide is implemented as a series of cards, each corresponding to a
+specific privacy setting. The main components are:
+
+-   **Desktop**: The user interface for Desktop is built using WebUI and is
+    located in
+    `chrome/browser/resources/settings/privacy_page/privacy_guide/`.
+-   **Android**: The Android-specific implementation is located in the `android/`
+    subdirectory.
+-   **Metrics**: The feature is instrumented with metrics to track user
+    interactions and settings changes. The metric enums are defined in
+    `privacy_guide.h`.
diff --git a/chrome/browser/profiles/BUILD.gn b/chrome/browser/profiles/BUILD.gn
index d607441d..476b24fb 100644
--- a/chrome/browser/profiles/BUILD.gn
+++ b/chrome/browser/profiles/BUILD.gn
@@ -382,6 +382,7 @@
       "//chrome/browser/ash/policy/dlp",
       "//chrome/browser/ash/policy/skyvault",
       "//chrome/browser/ash/scanner",
+      "//chrome/browser/certificate_provider",
       "//chrome/browser/chromeos/cros_apps",
       "//chrome/browser/chromeos/enterprise/cloud_storage",
       "//chrome/browser/chromeos/extensions/telemetry/api:keyed_service_factory",
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index f0552163..4264c6d 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/autocomplete/in_memory_url_index_factory.h"
 #include "chrome/browser/autocomplete/provider_state_service_factory.h"
 #include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
+#include "chrome/browser/autofill/account_setting_service_factory.h"
 #include "chrome/browser/autofill/autocomplete_history_manager_factory.h"
 #include "chrome/browser/autofill/autofill_ai_model_cache_factory.h"
 #include "chrome/browser/autofill/autofill_ai_model_executor_factory.h"
@@ -220,7 +221,6 @@
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/tpcd/experiment/eligibility_service_factory.h"
 #include "chrome/browser/tpcd/heuristics/opener_heuristic_service_factory.h"
-#include "chrome/browser/tpcd/support/origin_trial_service_factory.h"
 #include "chrome/browser/tpcd/support/top_level_trial_service_factory.h"
 #include "chrome/browser/tpcd/support/tpcd_support_service_factory.h"
 #include "chrome/browser/translate/translate_ranker_factory.h"
@@ -672,6 +672,7 @@
   AccountInvestigatorFactory::GetInstance();
   AccountPasswordStoreFactory::GetInstance();
   AccountReconcilorFactory::GetInstance();
+  autofill::AccountSettingServiceFactory::GetInstance();
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
   AccountsPolicyManagerFactory::GetInstance();
 #endif
@@ -1407,7 +1408,6 @@
 #endif
   TopSitesFactory::GetInstance();
   tpcd::experiment::EligibilityServiceFactory::GetInstance();
-  tpcd::trial::OriginTrialServiceFactory::GetInstance();
   tpcd::trial::TpcdTrialServiceFactory::GetInstance();
   tpcd::trial::TopLevelTrialServiceFactory::GetInstance();
   TrackingProtectionOnboardingFactory::GetInstance();
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 75a02ffd..22494fe 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -105,7 +105,6 @@
 #include "chrome/browser/startup_data.h"
 #include "chrome/browser/storage/storage_notification_service_factory.h"
 #include "chrome/browser/sync/sync_service_factory.h"
-#include "chrome/browser/tpcd/support/origin_trial_service_factory.h"
 #include "chrome/browser/tpcd/support/top_level_trial_service_factory.h"
 #include "chrome/browser/tpcd/support/tpcd_support_service_factory.h"
 #include "chrome/browser/transition_manager/full_browser_transition_manager.h"
@@ -865,13 +864,12 @@
   // as it depends on the default StoragePartition being initialized.
   GetOriginTrialsControllerDelegate();
 
-  // The TpcdTrialService, TopLevelTrialService, and OriginTrialService for
+  // The TpcdTrialService and TopLevelTrialService for
   // third-party cookie deprecation must be created with the profile, but after
   // the initialization of the OriginTrialsControllerDelegate, as it depends on
   // it.
   tpcd::trial::TpcdTrialServiceFactory::GetForProfile(this);
   tpcd::trial::TopLevelTrialServiceFactory::GetForProfile(this);
-  tpcd::trial::OriginTrialServiceFactory::GetForProfile(this);
 }
 
 base::FilePath ProfileImpl::last_selected_directory() {
diff --git a/chrome/browser/profiles/profile_keyed_service_browsertest.cc b/chrome/browser/profiles/profile_keyed_service_browsertest.cc
index fd96249..12a5d22 100644
--- a/chrome/browser/profiles/profile_keyed_service_browsertest.cc
+++ b/chrome/browser/profiles/profile_keyed_service_browsertest.cc
@@ -195,7 +195,6 @@
           network::features::kBrowsingTopics,
           blink::features::kBuiltInAIAPI,
           extensions_features::kForceWebRequestProxyForTest,
-          net::features::kTopLevelTpcdOriginTrial,
           net::features::kTpcdTrialSettings,
           net::features::kTopLevelTpcdTrialSettings,
           network::features::kReduceAcceptLanguage,
@@ -748,7 +747,6 @@
     "OneTimePermissionsTrackerKeyedService",
     "OperationManager",
     "OptimizationGuideKeyedService",
-    "OriginTrialService",
 #if !BUILDFLAG(IS_CHROMEOS)
     // TODO(crbug.com/374351946): Investigate if this is necessary on CrOS.
     "PageContentAnnotationsService",
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/login_state.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/login_state.d.ts
index 6506e24..06a79d2 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/login_state.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/login_state.d.ts
@@ -20,6 +20,7 @@
       export enum ProfileType {
         SIGNIN_PROFILE = 'SIGNIN_PROFILE',
         USER_PROFILE = 'USER_PROFILE',
+        LOCK_PROFILE = 'LOCK_PROFILE',
       }
 
       export enum SessionState {
diff --git a/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.html b/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.html
index 8bad9bc..7fa09dce 100644
--- a/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.html
+++ b/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.html
@@ -9,12 +9,18 @@
   /* Light mode not on AIM results */
   :host(:not([is-on-aim-results]):not([dark-mode])) {
     --ntp-composebox-background-color: #f3f5f6;
+    --color-new-tab-page-composebox-font: #0a0a0a;
+    --color-new-tab-page-composebox-hover: #e9ebf0;
+    --color-new-tab-page-composebox-cancel-button: #0a0a0a;
     --search-background-color: white;
   }
 
   /* Light mode on AIM results */
   :host([is-on-aim-results]:not([dark-mode])) {
     --ntp-composebox-background-color: #f3f5f6;
+    --color-new-tab-page-composebox-font: #0a0a0a;
+    --color-new-tab-page-composebox-hover: #e9ebf0;
+    --color-new-tab-page-composebox-cancel-button: #0a0a0a;
     --search-background-color: white;
   }
 
@@ -23,12 +29,16 @@
     --ntp-composebox-background-color: #28292a;
     --color-new-tab-page-composebox-cancel-button: #bfbfbf;
     --color-new-tab-page-composebox-hover: #3e3f43;
+    --color-new-tab-page-composebox-font: #e6e8f0;
     --search-background-color: #1f1f1f;
   }
 
   /* Dark mode on AIM results */
   :host([is-on-aim-results][dark-mode]) {
     --ntp-composebox-background-color: #1d1e26;
+    --color-new-tab-page-composebox-font: #e6e8f0;
+    --color-new-tab-page-composebox-hover: #3e3f43;
+    --color-new-tab-page-composebox-cancel-button: #bfbfbf;
     --search-background-color: #101218;
   }
 
@@ -295,13 +305,13 @@
     position: absolute;
   }
 
-  :host([enable-lens-aim-suggestions]) #composeboxContainer:has(ntp-composebox[show-dropdown_][expanded_]) {
+  :host([enable-lens-aim-suggestions])
+    #composeboxContainer:has(ntp-composebox[show-dropdown_][expanded_]) {
     background-color: var(--search-background-color);
     pointer-events: auto;
     inset-block-start: var(--aim-header-height);
   }
 
-
   #composebox {
     --expanded-border-radius: 24px;
     --input-bottom-spacing: 16px;
@@ -332,20 +342,17 @@
 
   #composebox::part(action-icon) {
     --cr-icon-button-size: 48px;
-    --cr-icon-button-icon-size: 24px;
-    --cr-icon-button-hover-background-color:
-      var(--color-new-tab-page-composebox-hover);
+    --cr-icon-button-hover-background-color: var(--color-new-tab-page-composebox-hover);
   }
 
   #composebox::part(cancel-icon) {
-    --cr-icon-button-fill-color:
-      var(--color-new-tab-page-composebox-cancel-button);
+    --cr-icon-button-icon-size: 24px;
+    --cr-icon-button-fill-color: var(--color-new-tab-page-composebox-cancel-button);
   }
 
   :host([show-lens-button]) #composebox::part(lens-icon) {
     --cr-icon-button-icon-size: 24px;
-    --cr-icon-button-fill-color:
-      var(--color-new-tab-page-composebox-lens-button);
+    --cr-icon-button-fill-color: var(--color-new-tab-page-composebox-lens-button);
     display: block;
   }
 
diff --git a/chrome/browser/resources/new_tab_footer/BUILD.gn b/chrome/browser/resources/new_tab_footer/BUILD.gn
index 9f3018b0..f92fd18 100644
--- a/chrome/browser/resources/new_tab_footer/BUILD.gn
+++ b/chrome/browser/resources/new_tab_footer/BUILD.gn
@@ -36,10 +36,14 @@
   mojo_files_deps = [
     "//chrome/browser/ui/webui/customize_buttons:mojo_bindings_ts__generator",
     "//chrome/browser/ui/webui/new_tab_footer:mojo_bindings_ts__generator",
+    "//chrome/browser/ui/webui/side_panel/customize_chrome:mojo_bindings_ts__generator",
+    "//components/ntp_tiles:mojo_bindings_ts__generator",
   ]
   mojo_files = [
     "$root_gen_dir/chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom-webui.ts",
     "$root_gen_dir/chrome/browser/ui/webui/new_tab_footer/new_tab_footer.mojom-webui.ts",
+    "$root_gen_dir/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom-webui.ts",
+    "$root_gen_dir/components/ntp_tiles/tile_type.mojom-webui.ts",
   ]
 
   ts_path_mappings =
diff --git a/chrome/browser/resources/new_tab_footer/app.ts b/chrome/browser/resources/new_tab_footer/app.ts
index fb99df1..740512f 100644
--- a/chrome/browser/resources/new_tab_footer/app.ts
+++ b/chrome/browser/resources/new_tab_footer/app.ts
@@ -17,8 +17,9 @@
 import {getHtml} from './app.html.js';
 import {NewTabFooterDocumentProxy} from './browser_proxy.js';
 import type {CustomizeButtonsDocumentCallbackRouter, CustomizeButtonsHandlerRemote} from './customize_buttons.mojom-webui.js';
-import {CustomizeChromeSection, SidePanelOpenTrigger} from './customize_buttons.mojom-webui.js';
+import {SidePanelOpenTrigger} from './customize_buttons.mojom-webui.js';
 import {CustomizeButtonsProxy} from './customize_buttons_proxy.js';
+import {CustomizeChromeSection} from './customize_chrome.mojom-webui.js';
 import type {BackgroundAttribution, ManagementNotice, NewTabFooterDocumentCallbackRouter, NewTabFooterHandlerInterface} from './new_tab_footer.mojom-webui.js';
 import {NewTabPageType} from './new_tab_footer.mojom-webui.js';
 import {WindowProxy} from './window_proxy.js';
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn
index b9f9209..647d99c 100644
--- a/chrome/browser/resources/new_tab_page/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -57,6 +57,7 @@
     "//chrome/browser/ui/webui/new_tab_page:mojo_bindings_ts__generator",
     "//chrome/browser/ui/webui/new_tab_page/ntp_promo:mojo_bindings_ts__generator",
     "//chrome/browser/ui/webui/ntp_microsoft_auth:shared_ts__generator",
+    "//chrome/browser/ui/webui/side_panel/customize_chrome:mojo_bindings_ts__generator",
     "//components/ntp_tiles:mojo_bindings_ts__generator",
     "//components/omnibox/composebox:mojo_bindings_ts__generator",
     "//components/tab_groups/public/mojom:mojo_bindings_ts__generator",
@@ -77,6 +78,7 @@
     "$root_gen_dir/chrome/browser/ui/webui/ntp_microsoft_auth/ntp_microsoft_auth_shared_ui.mojom-webui.ts",
     "$root_gen_dir/chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom-webui.ts",
     "$root_gen_dir/ui/webui/resources/cr_components/composebox/composebox.mojom-webui.ts",
+    "$root_gen_dir/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom-webui.ts",
     "$root_gen_dir/chrome/browser/ui/webui/new_tab_page/ntp_promo/ntp_promo.mojom-webui.ts",
     "$root_gen_dir/components/omnibox/composebox/composebox_query.mojom-webui.ts",
     "$root_gen_dir/components/ntp_tiles/tile_type.mojom-webui.ts",
diff --git a/chrome/browser/resources/new_tab_page/app.ts b/chrome/browser/resources/new_tab_page/app.ts
index 4f27107..1b81304 100644
--- a/chrome/browser/resources/new_tab_page/app.ts
+++ b/chrome/browser/resources/new_tab_page/app.ts
@@ -33,8 +33,9 @@
 import {getHtml} from './app.html.js';
 import {BackgroundManager} from './background_manager.js';
 import type {CustomizeButtonsDocumentCallbackRouter, CustomizeButtonsHandlerRemote} from './customize_buttons.mojom-webui.js';
-import {CustomizeChromeSection, SidePanelOpenTrigger} from './customize_buttons.mojom-webui.js';
+import {SidePanelOpenTrigger} from './customize_buttons.mojom-webui.js';
 import {CustomizeButtonsProxy} from './customize_buttons_proxy.js';
+import {CustomizeChromeSection} from './customize_chrome.mojom-webui.js';
 import {CustomizeDialogPage} from './customize_dialog_types.js';
 import type {IframeElement} from './iframe.js';
 import type {LogoElement} from './logo.js';
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page.gni b/chrome/browser/resources/new_tab_page/new_tab_page.gni
index a65daab..a424e1c 100644
--- a/chrome/browser/resources/new_tab_page/new_tab_page.gni
+++ b/chrome/browser/resources/new_tab_page/new_tab_page.gni
@@ -50,6 +50,7 @@
   mojo_js_files = [
     "calendar_data.mojom-webui.js",
     "ntp_promo.mojom-webui.js",
+    "customize_chrome.mojom-webui.js",
     "customize_buttons.mojom-webui.js",
     "drive_suggestion.mojom-webui.js",
     "file_suggestion.mojom-webui.js",
diff --git a/chrome/browser/resources/omnibox_popup/app.css b/chrome/browser/resources/omnibox_popup/app.css
index e68972c..9b6afbd 100644
--- a/chrome/browser/resources/omnibox_popup/app.css
+++ b/chrome/browser/resources/omnibox_popup/app.css
@@ -9,7 +9,8 @@
 
 :host {
   --cr-searchbox-icon-container-size: 28px;
-  --cr-searchbox-match-icon-container-background-fallback: var(--color-omnibox-results-background-hovered);
+  --cr-searchbox-match-icon-container-background-fallback:
+      var(--color-omnibox-results-background-hovered);
   --cr-searchbox-match-padding: 6px;
   --cr-searchbox-match-padding-inline-start: 16px;
   --cr-searchbox-match-text-padding-inline-start: 12px;
@@ -21,12 +22,16 @@
   /* Colors used in the cr-searchbox results dropdown */
   --color-searchbox-answer-icon-background: var(--color-omnibox-answer-icon-g-m3-background);
   --color-searchbox-answer-icon-foreground: var(--color-omnibox-answer-icon-g-m3-foreground);
-  --color-searchbox-results-action-chip-focus-outline: var(--color-omnibox-results-button-icon-selected);
-  --color-searchbox-results-action-chip-icon: var(--color-omnibox-results-button-icon);
+  --color-searchbox-results-action-chip-focus-outline:
+      var(--color-omnibox-results-button-icon-selected);
   --color-searchbox-results-action-chip: var(--color-omnibox-results-button-border);
+  --color-searchbox-results-action-chip-icon: var(--color-omnibox-results-button-icon);
   --color-searchbox-results-background-hovered: var(--color-omnibox-results-background-hovered);
   --color-searchbox-results-background: var(--color-omnibox-results-background);
-  --color-searchbox-results-button-hover: var(--color-omnibox-results-icon-hovered);
+  --color-searchbox-results-button-hover:
+      var(--color-omnibox-results-button-ink-drop-row-hovered);
+  --color-searchbox-results-button-selected-hover:
+      var(--color-omnibox-results-button-ink-drop-row-selected);
   --color-searchbox-results-dim-selected: var(--color-omnibox-results-text-dimmed-selected);
   --color-searchbox-results-focus-indicator: var(--color-omnibox-results-focus-indicator);
   --color-searchbox-results-foreground-dimmed: var(--color-omnibox-results-text-dimmed);
diff --git a/chrome/browser/resources/pdf/pdf_viewer.ts b/chrome/browser/resources/pdf/pdf_viewer.ts
index 68f57aa..5a70653 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.ts
+++ b/chrome/browser/resources/pdf/pdf_viewer.ts
@@ -30,7 +30,7 @@
 import {listenOnce} from 'chrome://resources/js/util.js';
 import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
-// <if expr="enable_pdf_ink2">
+// <if expr="enable_pdf_ink2 or enable_pdf_save_to_drive">
 import {BeforeUnloadProxyImpl} from './before_unload_proxy.js';
 // </if>
 import type {Bookmark} from './bookmark_type.js';
@@ -165,7 +165,7 @@
 // LINT.ThenChange(//chrome/browser/resources/pdf/pdf_embedder.css:PdfBackgroundColor, //components/pdf/common/pdf_util.cc:PdfBackgroundColor)
 // clang-format on
 
-// <if expr="enable_pdf_ink2">
+// <if expr="enable_pdf_ink2 or enable_pdf_save_to_drive">
 function isEditedSaveRequestType(requestType: SaveRequestType): boolean {
   return requestType === SaveRequestType.ANNOTATION ||
       requestType === SaveRequestType.EDITED;
@@ -350,6 +350,8 @@
   // `hasSavedEdits_` is true if the PDF has been saved with edits. Additional
   // changes or saves of the document will not update this property.
   private hasSavedEdits_: boolean = false;
+  // </if>
+  // <if expr="enable_pdf_ink2 or enable_pdf_save_to_drive">
   // `hasUnsavedEdits_` is set whenever the user makes edits to the PDF that
   // have not been saved. This is used to determine whether to enable the
   // beforeunload dialog when the user navigates away with unsaved changes.
@@ -379,6 +381,8 @@
   private pluginController_: PluginController = PluginController.getInstance();
   // <if expr="enable_pdf_ink2">
   private restoreAnnotationMode_: AnnotationMode = AnnotationMode.OFF;
+  // </if>
+  // <if expr="enable_pdf_ink2 or enable_pdf_save_to_drive">
   private showBeforeUnloadDialog_: boolean = false;
   // </if>
   protected accessor showPasswordDialog_: boolean = false;
@@ -431,21 +435,19 @@
     // <if expr="enable_pdf_save_to_drive">
     const changedPrivateProperties =
         changedProperties as Map<PropertyKey, unknown>;
-    if (changedPrivateProperties.has('saveToDriveState_') &&
-        changedPrivateProperties.get('saveToDriveState_') ===
-            SaveToDriveState.UPLOADING &&
-        this.saveToDriveState_ !== SaveToDriveState.UNINITIALIZED) {
-      this.getSaveToDriveBubble_().showAt(
-          this.$.toolbar.getSaveToDriveBubbleAnchor(),
-          /*autoDismiss=*/ true);
+    if (changedPrivateProperties.has('saveToDriveState_')) {
+      this.onSaveToDriveStateChanged_(
+          changedPrivateProperties.get('saveToDriveState_') as
+          SaveToDriveState);
     }
     // </if>
   }
 
-  // <if expr="enable_pdf_ink2">
+  // <if expr="enable_pdf_ink2 or enable_pdf_save_to_drive">
   override connectedCallback() {
     super.connectedCallback();
     this.tracker.add(window, 'beforeunload', this.onBeforeUnload_.bind(this));
+    // <if expr="enable_pdf_ink2">
     const mediaQuery = window.matchMedia('(min-width: 960px)');
     this.useSidePanelForInk_ = mediaQuery.matches;
     this.tracker.add(mediaQuery, 'change', () => {
@@ -458,13 +460,14 @@
                                        UserAction.OPEN_INK2_BOTTOM_TOOLBAR);
       }
     });
+    // </if> enable_pdf_ink2
   }
 
   override disconnectedCallback() {
     this.tracker.removeAll();
     super.disconnectedCallback();
   }
-  // </if> enable_pdf_ink2
+  // </if> enable_pdf_ink2 or enable_pdf_save_to_drive
 
   getBackgroundColor(): number {
     return BACKGROUND_COLOR;
@@ -1405,6 +1408,50 @@
     assert(bubble);
     return bubble;
   }
+
+  private onSaveToDriveStateChanged_(oldSaveToDriveState: SaveToDriveState) {
+    // Transition from UNINITIALIZED to UPLOADING.
+    if (oldSaveToDriveState === SaveToDriveState.UNINITIALIZED &&
+        this.isSaveToDriveUploading_()) {
+      // Block unloading the window if upload is in progress.
+      this.setShowBeforeUnloadDialog_(true);
+      if (isEditedSaveRequestType(this.saveToDriveRequestType_)) {
+        this.hasUnsavedEdits_ = false;
+      }
+      return;
+    }
+    // Transition from a final state (COMPLETE, or any error state) to
+    // UNINITIALIZED.
+    if (oldSaveToDriveState !== SaveToDriveState.UPLOADING) {
+      // TODO(crbug.com/427449996): Add an assertion to check that the current
+      // state is UNINITIALIZED. Also update the tests to accommodate the
+      // change.
+      this.setShowBeforeUnloadDialog_(this.hasUnsavedEdits_);
+      return;
+    }
+    // Transition from UPLOADING to SUCCESS, cancelled, or error state.
+    if (this.saveToDriveState_ === SaveToDriveState.SUCCESS) {
+      this.onSaveSuccessful_(this.saveToDriveRequestType_);
+    } else {
+      // TODO(crbug.com/427449996): Fix an edge case where beforeunload dialog
+      // is still blocking if an EDITED upload is cancelled after a successful
+      // EDITED disk save. This could happen in the following order:
+      // 1. Make an edit.
+      // 2. Initiate an EDITED save to Drive.
+      // 3. Initiate an EDITED disk save.
+      // 4. Cancel the EDITED save to Drive upload.
+      // 5. `hasUnsavedEdits_` is restored to true from step 4.
+      // <if expr="enable_pdf_ink2">
+      this.onSaveFailedOrCancelled_(this.saveToDriveRequestType_);
+      // </if>
+      if (this.saveToDriveState_ === SaveToDriveState.UNINITIALIZED) {
+        return;
+      }
+    }
+    this.getSaveToDriveBubble_().showAt(
+        this.$.toolbar.getSaveToDriveBubbleAnchor(),
+        /*autoDismiss=*/ true);
+  }
   // </if> enable_pdf_save_to_drive
 
   protected onChangePage_(e: CustomEvent<ChangePageDetail>) {
@@ -1457,7 +1504,7 @@
     // strokes have updated. If the user hasn't saved, only show the
     // beforeunload dialog if there's edits.
     this.setShowBeforeUnloadDialog_(
-        this.hasSavedEdits_ || this.hasUnsavedEdits_);
+        this.hasSavedEdits_ || this.shouldShowBeforeUnloadDialog_());
   }
   // </if>
 
@@ -1727,15 +1774,14 @@
    * Performs required tasks after a successful save.
    */
   private onSaveSuccessful_(requestType: SaveRequestType) {
-    // <if expr="enable_pdf_ink2">
-    // Unblock closing the window if there are no unsaved edits.
-    this.setShowBeforeUnloadDialog_(this.hasUnsavedEdits_);
+    // <if expr="enable_pdf_ink2 or enable_pdf_save_to_drive">
+    this.setShowBeforeUnloadDialog_(this.shouldShowBeforeUnloadDialog_());
     this.hasSavedEdits_ =
         this.hasSavedEdits_ || requestType === SaveRequestType.EDITED;
     // </if>
   }
 
-  // <if expr="enable_pdf_ink2">
+  // <if expr="enable_pdf_ink2 or enable_pdf_save_to_drive">
   /**
    * Performs required tasks after a failed or cancelled save.
    */
@@ -1745,7 +1791,20 @@
     if (isEditedSaveRequestType(requestType)) {
       this.hasUnsavedEdits_ = true;
     }
-    this.setShowBeforeUnloadDialog_(this.hasUnsavedEdits_);
+    this.setShowBeforeUnloadDialog_(this.shouldShowBeforeUnloadDialog_());
+  }
+
+  /**
+   * Returns whether the beforeunload dialog should be shown.
+   */
+  private shouldShowBeforeUnloadDialog_(): boolean {
+    let showBeforeUnloadDialog = this.hasUnsavedEdits_;
+    // <if expr="enable_pdf_save_to_drive">
+    // If Save to Drive is uploading, block closing the window.
+    showBeforeUnloadDialog =
+        showBeforeUnloadDialog || this.isSaveToDriveUploading_();
+    // </if>
+    return showBeforeUnloadDialog;
   }
   // </if>
 
@@ -1852,15 +1911,15 @@
   }
   // </if>
 
-  // <if expr="enable_pdf_ink2">
+  // <if expr="enable_pdf_ink2 or enable_pdf_save_to_drive">
   /**
    * Handles the `BeforeUnloadEvent` event.
    * @param event The `BeforeUnloadEvent` object representing the event.
    */
   private onBeforeUnload_(event: BeforeUnloadEvent) {
-    // When a user tries to leave PDF with unsaved changes, show the 'Leave
-    // site' dialog. OOPIF PDF only, since MimeHandler handles the beforeunload
-    // event instead.
+    // When a user tries to leave PDF with unsaved changes or when Save to Drive
+    // is in progress, show the 'Leave site' dialog. OOPIF PDF only, since
+    // MimeHandler handles the beforeunload event instead.
     if (this.pdfOopifEnabled && this.showBeforeUnloadDialog_) {
       BeforeUnloadProxyImpl.getInstance().preventDefault(event);
     }
diff --git a/chrome/browser/resources/privacy_sandbox/internals/content_settings_groups.ts b/chrome/browser/resources/privacy_sandbox/internals/content_settings_groups.ts
index 2069510..04b50a3e 100644
--- a/chrome/browser/resources/privacy_sandbox/internals/content_settings_groups.ts
+++ b/chrome/browser/resources/privacy_sandbox/internals/content_settings_groups.ts
@@ -35,7 +35,6 @@
           ContentSettingsType.STORAGE_ACCESS_HEADER_ORIGIN_TRIAL,
           ContentSettingsType.THIRD_PARTY_STORAGE_PARTITIONING,
           ContentSettingsType.TOP_LEVEL_STORAGE_ACCESS,
-          ContentSettingsType.TOP_LEVEL_TPCD_ORIGIN_TRIAL,
           ContentSettingsType.TOP_LEVEL_TPCD_TRIAL,
           ContentSettingsType.TPCD_HEURISTICS_GRANTS,
           ContentSettingsType.TPCD_METADATA_GRANTS,
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn
index a705901..a3be4a9 100644
--- a/chrome/browser/resources/settings/BUILD.gn
+++ b/chrome/browser/resources/settings/BUILD.gn
@@ -93,6 +93,7 @@
     "clear_browsing_data_dialog/history_deletion_dialog.ts",
     "clear_browsing_data_dialog/other_google_data_dialog.ts",
     "clear_browsing_data_dialog/passwords_deletion_dialog.ts",
+    "controls/collapse_radio_button.ts",
     "controls/controlled_button.ts",
     "controls/controlled_radio_button.ts",
     "controls/settings_checkbox.ts",
@@ -126,7 +127,6 @@
     "performance_page/tab_discard/exception_list.ts",
     "performance_page/tab_discard/exception_tabbed_add_dialog.ts",
     "privacy_page/anti_abuse_page.ts",
-    "privacy_page/collapse_radio_button.ts",
     "privacy_page/cookies_page.ts",
     "privacy_page/do_not_track_toggle.ts",
     "privacy_page/fingerprint_progress_arc.ts",
@@ -229,6 +229,7 @@
     "site_settings/popups_page.ts",
     "site_settings/protected_content_page.ts",
     "site_settings/protocol_handlers.ts",
+    "site_settings/recent_site_permissions.ts",
     "site_settings/sensors_page.ts",
     "site_settings/serial_ports_page.ts",
     "site_settings/settings_category_default_radio_group.ts",
@@ -239,6 +240,8 @@
     "site_settings/site_entry.ts",
     "site_settings/site_list_entry.ts",
     "site_settings/site_list.ts",
+    "site_settings/site_settings_list.ts",
+    "site_settings/site_settings_page.ts",
     "site_settings/sound_page.ts",
     "site_settings/storage_access_page.ts",
     "site_settings/storage_access_site_list_entry.ts",
@@ -251,9 +254,6 @@
     "site_settings/web_printing_page.ts",
     "site_settings/window_management_page.ts",
     "site_settings/zoom_levels.ts",
-    "site_settings_page/recent_site_permissions.ts",
-    "site_settings_page/site_settings_list.ts",
-    "site_settings_page/site_settings_page.ts",
     "your_saved_info_page/your_saved_info_page.ts",
     "your_saved_info_page/your_saved_info_page_index.ts",
   ]
@@ -424,8 +424,8 @@
     "privacy_page/privacy_guide/privacy_guide_fragment_shared.css",
     "search_page/search_engine_entry.css",
     "site_settings/clear_storage_dialog_shared.css",
+    "site_settings/site_review_shared.css",
     "site_settings/site_settings_shared.css",
-    "site_settings_page/site_review_shared.css",
   ]
 
   ts_composite = true
diff --git a/chrome/browser/resources/settings/autofill_page/autofill_section.ts b/chrome/browser/resources/settings/autofill_page/autofill_section.ts
index d737f80..4a9af7a 100644
--- a/chrome/browser/resources/settings/autofill_page/autofill_section.ts
+++ b/chrome/browser/resources/settings/autofill_page/autofill_section.ts
@@ -265,9 +265,16 @@
       }
 
       this.autofillManager_.removeAddress(this.activeAddress!.guid as string);
-      getAnnouncerInstance().announce(loadTimeData.getString(
-          isHomeOrWorkAddress ? 'homeAndWorkAddressRemovedMessage' :
-                                'addressRemovedMessage'));
+      if (isHomeOrWorkAddress) {
+        getAnnouncerInstance().announce(
+            loadTimeData.getString('homeAndWorkAddressRemovedMessage'));
+      } else if (this.isAccountNameEmailAddress_(this.activeAddress!)) {
+        getAnnouncerInstance().announce(
+            loadTimeData.getString('nameEmailAddressRemovedMessage'));
+      } else {
+        getAnnouncerInstance().announce(
+            loadTimeData.getString('addressRemovedMessage'));
+      }
     }
     chrome.metricsPrivate.recordBoolean(
         'Autofill.ProfileDeleted.Settings',
diff --git a/chrome/browser/resources/settings/privacy_page/collapse_radio_button.html b/chrome/browser/resources/settings/controls/collapse_radio_button.html
similarity index 100%
rename from chrome/browser/resources/settings/privacy_page/collapse_radio_button.html
rename to chrome/browser/resources/settings/controls/collapse_radio_button.html
diff --git a/chrome/browser/resources/settings/privacy_page/collapse_radio_button.ts b/chrome/browser/resources/settings/controls/collapse_radio_button.ts
similarity index 100%
rename from chrome/browser/resources/settings/privacy_page/collapse_radio_button.ts
rename to chrome/browser/resources/settings/controls/collapse_radio_button.ts
diff --git a/chrome/browser/resources/settings/lazy_load.ts b/chrome/browser/resources/settings/lazy_load.ts
index 75d56d3..c0f9998 100644
--- a/chrome/browser/resources/settings/lazy_load.ts
+++ b/chrome/browser/resources/settings/lazy_load.ts
@@ -67,7 +67,6 @@
 import './site_settings/microphone_page.js';
 import './site_settings/midi_devices_page.js';
 import './site_settings/notifications_page.js';
-import './site_settings_page/site_settings_page.js';
 import './site_settings/payment_handler_page.js';
 import './site_settings/pdf_documents_page.js';
 import './site_settings/popups_page.js';
@@ -79,6 +78,7 @@
 import './site_settings/site_data.js';
 import './site_settings/site_details.js';
 import './site_settings/site_details_permission_device_entry.js';
+import './site_settings/site_settings_page.js';
 // <if expr="is_chromeos">
 import './site_settings/smart_card_readers_page.js';
 // </if>
@@ -186,6 +186,7 @@
 export {SettingsHistoryDeletionDialogElement} from './clear_browsing_data_dialog/history_deletion_dialog.js';
 export {SettingsOtherGoogleDataDialogElement} from './clear_browsing_data_dialog/other_google_data_dialog.js';
 export {SettingsPasswordsDeletionDialogElement} from './clear_browsing_data_dialog/passwords_deletion_dialog.js';
+export {SettingsCollapseRadioButtonElement} from './controls/collapse_radio_button.js';
 export {ControlledButtonElement} from './controls/controlled_button.js';
 export {SettingsCheckboxElement} from './controls/settings_checkbox.js';
 export {SettingsRadioGroupElement} from './controls/settings_radio_group.js';
@@ -217,7 +218,6 @@
 export {SettingsSyncPageElement} from './people_page/sync_page.js';
 export {NetworkPredictionOptions} from './performance_page/constants.js';
 export {SettingsAntiAbusePageElement} from './privacy_page/anti_abuse_page.js';
-export {SettingsCollapseRadioButtonElement} from './privacy_page/collapse_radio_button.js';
 export {SettingsCookiesPageElement} from './privacy_page/cookies_page.js';
 export {CrLottieElement} from './privacy_page/cr_lottie.js';
 export {SettingsDoNotTrackToggleElement} from './privacy_page/do_not_track_toggle.js';
@@ -243,8 +243,8 @@
 export {SetPinDialogPage, SettingsSecurityKeysSetPinDialogElement} from './privacy_page/security_keys_set_pin_dialog.js';
 export {SecurityKeysSubpageElement} from './privacy_page/security_keys_subpage.js';
 export {HttpsFirstModeSetting, SafeBrowsingSetting, SettingsSecurityPageElement} from './privacy_page/security_page.js';
-export {SecuritySettingsBundleSetting, SettingsSecurityPageV2Element} from './privacy_page/security_page_v2.js';
 export {SecurityPageFeatureRowElement} from './privacy_page/security_page_feature_row.js';
+export {SecuritySettingsBundleSetting, SettingsSecurityPageV2Element} from './privacy_page/security_page_v2.js';
 export {SettingsPrivacySandboxAdMeasurementSubpageElement} from './privacy_sandbox/privacy_sandbox_ad_measurement_subpage.js';
 export {SettingsPrivacySandboxFledgeSubpageElement} from './privacy_sandbox/privacy_sandbox_fledge_subpage.js';
 export {PrivacySandboxInterestItemElement} from './privacy_sandbox/privacy_sandbox_interest_item.js';
@@ -283,6 +283,7 @@
 export {PdfDocumentsPageElement} from './site_settings/pdf_documents_page.js';
 export {ProtectedContentPageElement} from './site_settings/protected_content_page.js';
 export {AppHandlerEntry, AppProtocolEntry, HandlerEntry, ProtocolEntry, ProtocolHandlersElement} from './site_settings/protocol_handlers.js';
+export {SettingsRecentSitePermissionsElement} from './site_settings/recent_site_permissions.js';
 export {SettingsCategoryDefaultRadioGroupElement} from './site_settings/settings_category_default_radio_group.js';
 export {SettingsSiteDataElement} from './site_settings/site_data.js';
 export {SiteDetailsElement} from './site_settings/site_details.js';
@@ -291,6 +292,8 @@
 export {SiteEntryElement} from './site_settings/site_entry.js';
 export {SiteListElement} from './site_settings/site_list.js';
 export {SiteListEntryElement} from './site_settings/site_list_entry.js';
+export {defaultSettingLabel} from './site_settings/site_settings_list.js';
+export {SettingsSiteSettingsPageElement} from './site_settings/site_settings_page.js';
 export {ChooserException, DefaultContentSetting, DefaultSettingSource, FileSystemGrant, OriginFileSystemGrants, OriginInfo, RawChooserException, RawSiteException, RecentSitePermissions, SiteException, SiteGroup, SiteSettingsPrefsBrowserProxy, SiteSettingsPrefsBrowserProxyImpl, StorageAccessEmbeddingException, StorageAccessSiteException, ThirdPartyCookieBlockingSetting, ZoomLevelEntry} from './site_settings/site_settings_prefs_browser_proxy.js';
 // <if expr="is_chromeos">
 export {SettingsSmartCardReadersPageElement} from './site_settings/smart_card_readers_page.js';
@@ -301,9 +304,6 @@
 export {V8PageElement} from './site_settings/v8_page.js';
 export {WebsiteUsageBrowserProxy, WebsiteUsageBrowserProxyImpl} from './site_settings/website_usage_browser_proxy.js';
 export {ZoomLevelsElement} from './site_settings/zoom_levels.js';
-export {SettingsRecentSitePermissionsElement} from './site_settings_page/recent_site_permissions.js';
-export {defaultSettingLabel} from './site_settings_page/site_settings_list.js';
-export {SettingsSiteSettingsPageElement} from './site_settings_page/site_settings_page.js';
 // <if expr="not is_chromeos">
 export {SettingsSystemPageElement} from './system_page/system_page.js';
 export {SystemPageBrowserProxy, SystemPageBrowserProxyImpl} from './system_page/system_page_browser_proxy.js';
diff --git a/chrome/browser/resources/settings/performance_page/speed_page.ts b/chrome/browser/resources/settings/performance_page/speed_page.ts
index 64ac82e..396545e4 100644
--- a/chrome/browser/resources/settings/performance_page/speed_page.ts
+++ b/chrome/browser/resources/settings/performance_page/speed_page.ts
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import '../controls/collapse_radio_button.js';
 import '../controls/settings_radio_group.js';
 import '../controls/settings_toggle_button.js';
 import 'chrome://resources/cr_elements/cr_shared_style.css.js';
 import '/shared/settings/prefs/prefs.js';
 import 'chrome://resources/cr_elements/cr_collapse/cr_collapse.js';
-import '../privacy_page/collapse_radio_button.js';
 import '../settings_columned_section.css.js';
 import '../settings_page/settings_section.js';
 import '../settings_shared.css.js';
@@ -17,10 +17,10 @@
 import {OpenWindowProxyImpl} from 'chrome://resources/js/open_window_proxy.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import type {SettingsCollapseRadioButtonElement} from '../controls/collapse_radio_button.js';
 import type {SettingsRadioGroupElement} from '../controls/settings_radio_group.js';
 import type {SettingsToggleButtonElement} from '../controls/settings_toggle_button.js';
 import {loadTimeData} from '../i18n_setup.js';
-import type {SettingsCollapseRadioButtonElement} from '../privacy_page/collapse_radio_button.js';
 
 import {NetworkPredictionOptions} from './constants.js';
 import {PerformanceBrowserProxyImpl, PerformanceFeedbackCategory} from './performance_browser_proxy.js';
diff --git a/chrome/browser/resources/settings/privacy_page/cookies_page.ts b/chrome/browser/resources/settings/privacy_page/cookies_page.ts
index ac6908e..621a29f 100644
--- a/chrome/browser/resources/settings/privacy_page/cookies_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/cookies_page.ts
@@ -12,15 +12,15 @@
 import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
 import 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
+import '../controls/collapse_radio_button.js';
+import '../controls/settings_radio_group.js';
 import '../controls/settings_toggle_button.js';
 import '../icons.html.js';
 import '../privacy_icons.html.js';
 import '../settings_page/settings_subpage.js';
 import '../settings_shared.css.js';
 import '../site_settings/site_list.js';
-import './collapse_radio_button.js';
 import './do_not_track_toggle.js';
-import '../controls/settings_radio_group.js';
 
 import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js';
 import type {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_cookies_fragment.ts b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_cookies_fragment.ts
index baaf6613f..52120b6 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_cookies_fragment.ts
+++ b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_cookies_fragment.ts
@@ -11,9 +11,9 @@
 import 'chrome://resources/cr_elements/cr_icon/cr_icon.js';
 import '/shared/settings/prefs/prefs.js';
 import './privacy_guide_fragment_shared.css.js';
+import '../../controls/collapse_radio_button.js';
 import '../../controls/settings_radio_group.js';
 import '../../icons.html.js';
-import '../../privacy_page/collapse_radio_button.js';
 
 import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_safe_browsing_fragment.ts b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_safe_browsing_fragment.ts
index ed1f815..695eaa9 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_safe_browsing_fragment.ts
+++ b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_safe_browsing_fragment.ts
@@ -11,9 +11,9 @@
 import 'chrome://resources/cr_elements/cr_icon/cr_icon.js';
 import '/shared/settings/prefs/prefs.js';
 import './privacy_guide_fragment_shared.css.js';
+import '../../controls/collapse_radio_button.js';
 import '../../controls/settings_radio_group.js';
 import '../../icons.html.js';
-import '../../privacy_page/collapse_radio_button.js';
 
 import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js';
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html
index 89dbc8c..95b225cc 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -71,10 +71,10 @@
         sub-label="$i18n{securityPageDescription}"
         on-click="onSecurityPageClick_"
         role-description="$i18n{subpageArrowRoleDescription}"></cr-link-row>
-    <cr-link-row id="permissionsLinkRow" start-icon="privacy:page-info"
+    <cr-link-row id="siteSettingsLinkRow" start-icon="privacy:page-info"
         class="hr" label="$i18n{siteSettings}"
-        sub-label="$i18n{permissionsPageDescription}"
-        on-click="onPermissionsPageClick_"
+        sub-label="$i18n{siteSettingsSublabel}"
+        on-click="onSiteSettingsLinkRowClick_"
         role-description="$i18n{subpageArrowRoleDescription}"></cr-link-row>
     <cr-toast id="deleteBrowsingDataToast" duration="5000">
       <div>[[dbdDeletionConfirmationToastLabel_]]</div>
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.ts b/chrome/browser/resources/settings/privacy_page/privacy_page.ts
index 90c45d13..f832d42a 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.ts
@@ -42,7 +42,7 @@
 export interface SettingsPrivacyPageElement {
   $: {
     clearBrowsingData: CrLinkRowElement,
-    permissionsLinkRow: CrLinkRowElement,
+    siteSettingsLinkRow: CrLinkRowElement,
     securityLinkRow: CrLinkRowElement,
   };
 }
@@ -165,7 +165,7 @@
     focusWithoutInk(toFocus);
   }
 
-  private onPermissionsPageClick_() {
+  private onSiteSettingsLinkRowClick_() {
     this.interactedWithPage_();
 
     Router.getInstance().navigateTo(routes.SITE_SETTINGS);
@@ -276,7 +276,7 @@
     }
 
     if (routes.SITE_SETTINGS) {
-      map.set(routes.SITE_SETTINGS.path, '#permissionsLinkRow');
+      map.set(routes.SITE_SETTINGS.path, '#siteSettingsLinkRow');
     }
 
     return map;
@@ -347,7 +347,7 @@
       case 'siteSettingsWebPrinting':
       case 'siteSettingsWindowManagement':
       case 'siteSettingsZoomLevels':
-        triggerId = 'permissionsLinkRow';
+        triggerId = 'siteSettingsLinkRow';
         break;
       case 'privacySandbox':
       case 'privacySandboxAdMeasurement':
diff --git a/chrome/browser/resources/settings/privacy_page/security_page.ts b/chrome/browser/resources/settings/privacy_page/security_page.ts
index 58c759a..25dd6d3 100644
--- a/chrome/browser/resources/settings/privacy_page/security_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/security_page.ts
@@ -6,7 +6,7 @@
 import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
 import 'chrome://resources/cr_elements/cr_shared_style.css.js';
-import './collapse_radio_button.js';
+import '../controls/collapse_radio_button.js';
 import '../controls/settings_radio_group.js';
 import '../controls/settings_toggle_button.js';
 import '../privacy_page/secure_dns.js';
@@ -28,6 +28,7 @@
 import {OpenWindowProxyImpl} from 'chrome://resources/js/open_window_proxy.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import type {SettingsCollapseRadioButtonElement} from '../controls/collapse_radio_button.js';
 import type {SettingsRadioGroupElement} from '../controls/settings_radio_group.js';
 import type {SettingsToggleButtonElement} from '../controls/settings_toggle_button.js';
 import {HatsBrowserProxyImpl, SecurityPageInteraction} from '../hats_browser_proxy.js';
@@ -43,7 +44,6 @@
 import {SiteSettingsPrefsBrowserProxyImpl} from '../site_settings/site_settings_prefs_browser_proxy.js';
 import {isSettingEnabled} from '../site_settings/site_settings_util.js';
 
-import type {SettingsCollapseRadioButtonElement} from './collapse_radio_button.js';
 import {getTemplate} from './security_page.html.js';
 
 /**
diff --git a/chrome/browser/resources/settings/site_settings/constants.ts b/chrome/browser/resources/settings/site_settings/constants.ts
index 61e4ce9..7b31f8d 100644
--- a/chrome/browser/resources/settings/site_settings/constants.ts
+++ b/chrome/browser/resources/settings/site_settings/constants.ts
@@ -8,7 +8,7 @@
  * individual permissions under Site Details should appear here.
  * This should be kept in sync with the |kContentSettingsTypeGroupNames| array
  * in chrome/browser/ui/webui/settings/site_settings_helper.cc. See
- * chrome/browser/resources/settings/site_settings_page/site_settings_page_util
+ * chrome/browser/resources/settings/site_settings/site_settings_page_util
  * for translations.
  */
 
@@ -208,7 +208,7 @@
 
 /**
  * Corresponds to the animation-duration CSS parameter defined in
- * chrome/browser/resources/settings/site_settings_page/site_review_shared.css.
+ * chrome/browser/resources/settings/site_settings/site_review_shared.css.
  * Set to be slightly higher, as we want to ensure that the animation is
  * finished before updating the model for the right visual effect.
  */
diff --git a/chrome/browser/resources/settings/site_settings/geolocation_page.ts b/chrome/browser/resources/settings/site_settings/geolocation_page.ts
index 5fbb78c..601f5d5 100644
--- a/chrome/browser/resources/settings/site_settings/geolocation_page.ts
+++ b/chrome/browser/resources/settings/site_settings/geolocation_page.ts
@@ -11,9 +11,9 @@
 import './category_setting_exceptions.js';
 import './settings_category_default_radio_group.js';
 import './site_settings_shared.css.js';
+import '../controls/collapse_radio_button.js';
 import '../controls/settings_radio_group.js';
 import '../privacy_icons.html.js';
-import '../privacy_page/collapse_radio_button.js';
 import '../settings_page/settings_subpage.js';
 import '../settings_shared.css.js';
 
diff --git a/chrome/browser/resources/settings/site_settings/notifications_page.ts b/chrome/browser/resources/settings/site_settings/notifications_page.ts
index a818f37..f6aaf140 100644
--- a/chrome/browser/resources/settings/site_settings/notifications_page.ts
+++ b/chrome/browser/resources/settings/site_settings/notifications_page.ts
@@ -18,8 +18,8 @@
 import './category_setting_exceptions.js';
 import './settings_category_default_radio_group.js';
 import './site_settings_shared.css.js';
+import '../controls/collapse_radio_button.js';
 import '../privacy_icons.html.js';
-import '../privacy_page/collapse_radio_button.js';
 import '../safety_hub/safety_hub_module.js';
 import '../settings_page/settings_subpage.js';
 import '../settings_shared.css.js';
diff --git a/chrome/browser/resources/settings/site_settings/protected_content_page.ts b/chrome/browser/resources/settings/site_settings/protected_content_page.ts
index 90c2929..3cf0a97 100644
--- a/chrome/browser/resources/settings/site_settings/protected_content_page.ts
+++ b/chrome/browser/resources/settings/site_settings/protected_content_page.ts
@@ -5,9 +5,9 @@
 import '/shared/settings/prefs/prefs.js';
 import 'chrome://resources/cr_elements/cr_shared_style.css.js';
 import 'chrome://resources/cr_elements/icons.html.js';
+import '../controls/collapse_radio_button.js';
 import '../controls/settings_radio_group.js';
 import '../privacy_icons.html.js';
-import '../privacy_page/collapse_radio_button.js';
 import '../settings_page/settings_subpage.js';
 import '../settings_shared.css.js';
 import './category_setting_exceptions.js';
diff --git a/chrome/browser/resources/settings/site_settings/protocol_handlers.ts b/chrome/browser/resources/settings/site_settings/protocol_handlers.ts
index f0fc3e3..d7c038e 100644
--- a/chrome/browser/resources/settings/site_settings/protocol_handlers.ts
+++ b/chrome/browser/resources/settings/site_settings/protocol_handlers.ts
@@ -16,9 +16,9 @@
 import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
 import 'chrome://resources/cr_elements/cr_shared_style.css.js';
 import 'chrome://resources/cr_elements/icons.html.js';
+import '../controls/collapse_radio_button.js';
 import '../controls/settings_toggle_button.js';
 import '../privacy_icons.html.js';
-import '../privacy_page/collapse_radio_button.js';
 import '../settings_page/settings_subpage.js';
 import '../settings_shared.css.js';
 import '../site_favicon.js';
diff --git a/chrome/browser/resources/settings/site_settings_page/recent_site_permissions.html b/chrome/browser/resources/settings/site_settings/recent_site_permissions.html
similarity index 100%
rename from chrome/browser/resources/settings/site_settings_page/recent_site_permissions.html
rename to chrome/browser/resources/settings/site_settings/recent_site_permissions.html
diff --git a/chrome/browser/resources/settings/site_settings_page/recent_site_permissions.ts b/chrome/browser/resources/settings/site_settings/recent_site_permissions.ts
similarity index 100%
rename from chrome/browser/resources/settings/site_settings_page/recent_site_permissions.ts
rename to chrome/browser/resources/settings/site_settings/recent_site_permissions.ts
diff --git a/chrome/browser/resources/settings/site_settings/settings_category_default_radio_group.ts b/chrome/browser/resources/settings/site_settings/settings_category_default_radio_group.ts
index 88081976..4dff4a9d 100644
--- a/chrome/browser/resources/settings/site_settings/settings_category_default_radio_group.ts
+++ b/chrome/browser/resources/settings/site_settings/settings_category_default_radio_group.ts
@@ -8,16 +8,16 @@
  * a certain category under Site Settings.
  */
 import '../settings_shared.css.js';
+import '../controls/collapse_radio_button.js';
 import '../controls/settings_radio_group.js';
-import '../privacy_page/collapse_radio_button.js';
 
 import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
 import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import type {SettingsCollapseRadioButtonElement} from '../controls/collapse_radio_button.js';
 import type {SettingsRadioGroupElement} from '../controls/settings_radio_group.js';
 import {loadTimeData} from '../i18n_setup.js';
-import type {SettingsCollapseRadioButtonElement} from '../privacy_page/collapse_radio_button.js';
 
 import {ContentSetting, ContentSettingsTypes} from './constants.js';
 import {getTemplate} from './settings_category_default_radio_group.html.js';
diff --git a/chrome/browser/resources/settings/site_settings/site_data.ts b/chrome/browser/resources/settings/site_settings/site_data.ts
index 78c5a75..823f4e81 100644
--- a/chrome/browser/resources/settings/site_settings/site_data.ts
+++ b/chrome/browser/resources/settings/site_settings/site_data.ts
@@ -10,8 +10,8 @@
 import '/shared/settings/prefs/prefs.js';
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import '../controls/collapse_radio_button.js';
 import '../controls/settings_radio_group.js';
-import '../privacy_page/collapse_radio_button.js';
 import '../settings_page/settings_subpage.js';
 import './site_list.js';
 import './site_settings_shared.css.js';
@@ -20,8 +20,8 @@
 import {focusWithoutInk} from 'chrome://resources/js/focus_without_ink.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import type {SettingsCollapseRadioButtonElement} from '../controls/collapse_radio_button.js';
 import type {SettingsRadioGroupElement} from '../controls/settings_radio_group.js';
-import type {SettingsCollapseRadioButtonElement} from '../privacy_page/collapse_radio_button.js';
 import {SettingsViewMixin} from '../settings_page/settings_view_mixin.js';
 
 import {ContentSetting, ContentSettingsTypes} from './constants.js';
diff --git a/chrome/browser/resources/settings/site_settings_page/site_review_shared.css b/chrome/browser/resources/settings/site_settings/site_review_shared.css
similarity index 100%
rename from chrome/browser/resources/settings/site_settings_page/site_review_shared.css
rename to chrome/browser/resources/settings/site_settings/site_review_shared.css
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_list.html b/chrome/browser/resources/settings/site_settings/site_settings_list.html
similarity index 100%
rename from chrome/browser/resources/settings/site_settings_page/site_settings_list.html
rename to chrome/browser/resources/settings/site_settings/site_settings_list.html
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_list.ts b/chrome/browser/resources/settings/site_settings/site_settings_list.ts
similarity index 100%
rename from chrome/browser/resources/settings/site_settings_page/site_settings_list.ts
rename to chrome/browser/resources/settings/site_settings/site_settings_list.ts
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html b/chrome/browser/resources/settings/site_settings/site_settings_page.html
similarity index 100%
rename from chrome/browser/resources/settings/site_settings_page/site_settings_page.html
rename to chrome/browser/resources/settings/site_settings/site_settings_page.html
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts b/chrome/browser/resources/settings/site_settings/site_settings_page.ts
similarity index 100%
rename from chrome/browser/resources/settings/site_settings_page/site_settings_page.ts
rename to chrome/browser/resources/settings/site_settings/site_settings_page.ts
diff --git a/chrome/browser/resources/settings/site_settings/v8_page.ts b/chrome/browser/resources/settings/site_settings/v8_page.ts
index d703160..70b6796b 100644
--- a/chrome/browser/resources/settings/site_settings/v8_page.ts
+++ b/chrome/browser/resources/settings/site_settings/v8_page.ts
@@ -4,8 +4,8 @@
 
 import './category_setting_exceptions.js';
 import './site_settings_shared.css.js';
+import '../controls/collapse_radio_button.js';
 import '../controls/settings_radio_group.js';
-import '../privacy_page/collapse_radio_button.js';
 import '../settings_page/settings_subpage.js';
 import '../settings_shared.css.js';
 
diff --git a/chrome/browser/resources/settings/site_settings_page/OWNERS b/chrome/browser/resources/settings/site_settings_page/OWNERS
deleted file mode 100644
index 14eb2acc..0000000
--- a/chrome/browser/resources/settings/site_settings_page/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-lyf@chromium.org
-rainhard@chromium.org
-sideyilmaz@chromium.org
-fsenra@google.com
diff --git a/chrome/browser/resources/side_panel/customize_chrome/app.ts b/chrome/browser/resources/side_panel/customize_chrome/app.ts
index f0b4b22..6bc760bf 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/app.ts
+++ b/chrome/browser/resources/side_panel/customize_chrome/app.ts
@@ -35,6 +35,7 @@
 import type {ThemesElement} from './themes.js';
 
 const SECTION_TO_SELECTOR = {
+  [CustomizeChromeSection.kUnspecified]: '',
   [CustomizeChromeSection.kAppearance]: '#appearance',
   [CustomizeChromeSection.kShortcuts]: '#shortcuts',
   [CustomizeChromeSection.kModules]: '#modules',
diff --git a/chrome/browser/resources/side_panel/read_anything/read_aloud/read_aloud_types.ts b/chrome/browser/resources/side_panel/read_anything/read_aloud/read_aloud_types.ts
index 3615ed1..e3489d2 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_aloud/read_aloud_types.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_aloud/read_aloud_types.ts
@@ -108,7 +108,7 @@
     if (!(other instanceof DomReadAloudNode)) {
       return false;
     }
-    return this.node.isEqualNode(other.node);
+    return this.node.isSameNode(other.node);
   }
 
   getText(): string {
diff --git a/chrome/browser/resources/watermark/app.css b/chrome/browser/resources/watermark/app.css
index b516fb9..fec6ad5 100644
--- a/chrome/browser/resources/watermark/app.css
+++ b/chrome/browser/resources/watermark/app.css
@@ -70,7 +70,7 @@
 }
 
 cr-slider {
-  width: 170px;
+  width: 160px;
   --cr-slider-active-color: #1F6DD2;
   --cr-slider-knob-color: #1A73E8;
   --cr-slider-container-color: #D2E3FC;
@@ -78,6 +78,19 @@
   line-height: 20px;
 }
 
+.slider-container {
+  display: flex;
+  align-items: center;
+}
+
+.slider-container > span {
+  color: #202124;
+  font-size: 13px;
+  width: 30px;
+  text-align: right;
+  display: inline-block;
+}
+
 cr-input.font-size-input.stroked {
   --cr-input-border-radius: 8px;
   width: 70px;
diff --git a/chrome/browser/resources/watermark/app.html.ts b/chrome/browser/resources/watermark/app.html.ts
index 21945a8b..0567e29 100644
--- a/chrome/browser/resources/watermark/app.html.ts
+++ b/chrome/browser/resources/watermark/app.html.ts
@@ -26,7 +26,7 @@
         <cr-input id="fontSizeInput" class="font-size-input stroked"
             aria-label="Font size" type="number"
             min="1"
-            max="500
+            max="500"
             .value="${this.fontSize_.toString()}"
             @keydown="${this.onFontSizeInputKeyDown_}"
             @value-changed="${this.onFontSizeChanged_}">
@@ -39,22 +39,28 @@
 
       <div class="control-row">
         <span>White outline opacity</span>
-        <cr-slider id="outlineOpacitySlider" aria-label="White outline opacity"
-            min="0" max="100"
-            .value="${this.outlineOpacity_}"
-            .ticks="${this.opacityTicks_}"
-            @cr-slider-value-changed="${this.onOutlineOpacityChanged_}">
-        </cr-slider>
+        <div class="slider-container">
+          <cr-slider id="outlineOpacitySlider" aria-label="White outline opacity"
+              min="0" max="100"
+              .value="${this.outlineOpacity_}"
+              .ticks="${this.opacityTicks_}"
+              @cr-slider-value-changed="${this.onOutlineOpacityChanged_}">
+          </cr-slider>
+          <span class="slider-percentage">${this.outlineOpacity_}%</span>
+        </div>
       </div>
 
       <div class="control-row">
         <span>Dark fill opacity</span>
-        <cr-slider id="fillOpacitySlider" aria-label="Dark fill opacity"
-            min="0" max="100"
-            .value="${this.fillOpacity_}"
-            .ticks="${this.opacityTicks_}"
-            @cr-slider-value-changed="${this.onFillOpacityChanged_}">
-        </cr-slider>
+        <div class="slider-container">
+          <cr-slider id="fillOpacitySlider" aria-label="Dark fill opacity"
+              min="0" max="100"
+              .value="${this.fillOpacity_}"
+              .ticks="${this.opacityTicks_}"
+              @cr-slider-value-changed="${this.onFillOpacityChanged_}">
+          </cr-slider>
+          <span class="slider-percentage">${this.fillOpacity_}%</span>
+        </div>
       </div>
     </div>
   `;
diff --git a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/SafeBrowsingSettingsFragment.java b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/SafeBrowsingSettingsFragment.java
index ec9611a..eedcc93 100644
--- a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/SafeBrowsingSettingsFragment.java
+++ b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/SafeBrowsingSettingsFragment.java
@@ -240,6 +240,9 @@
             case SettingsAccessPoint.TAILORED_SECURITY:
                 metricsSuffix = "TailoredSecurity";
                 break;
+            case SettingsAccessPoint.TIPS_NOTIFICATIONS_PROMO:
+                metricsSuffix = "TipsNotificationsPromo";
+                break;
             default:
                 assert false : "Should not be reached.";
                 metricsSuffix = "";
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index 67aac0d..74bc053d 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -731,7 +731,8 @@
     content::WebContents* web_contents) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (IsIncognito() && !WebUIContentInfoSingleton::HasListener()) {
+  if (IsIncognito() &&
+      !WebUIContentInfoSingleton::GetInstance()->HasListener()) {
     return;
   }
 
@@ -777,7 +778,8 @@
     PasswordReuseDialogInteraction::InteractionResult interaction_result) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (IsIncognito() && !WebUIContentInfoSingleton::HasListener()) {
+  if (IsIncognito() &&
+      !WebUIContentInfoSingleton::GetInstance()->HasListener()) {
     return;
   }
 
@@ -805,7 +807,8 @@
     PasswordReuseLookup::LookupResult result) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (IsIncognito() && !WebUIContentInfoSingleton::HasListener()) {
+  if (IsIncognito() &&
+      !WebUIContentInfoSingleton::GetInstance()->HasListener()) {
     return;
   }
 
@@ -835,7 +838,8 @@
         const std::string& verdict_token) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (IsIncognito() && !WebUIContentInfoSingleton::HasListener()) {
+  if (IsIncognito() &&
+      !WebUIContentInfoSingleton::GetInstance()->HasListener()) {
     return;
   }
 
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
index 3110120..597d7ab2 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
@@ -65,6 +65,7 @@
   void AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OptimizationTarget optimization_target,
       const std::optional<optimization_guide::proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       optimization_guide::OptimizationTargetModelObserver* observer) override {
     if (optimization_target ==
         optimization_guide::proto::OPTIMIZATION_TARGET_CLIENT_SIDE_PHISHING) {
diff --git a/chrome/browser/safe_browsing/notification_content_detection/notification_content_detection_service_browsertest.cc b/chrome/browser/safe_browsing/notification_content_detection/notification_content_detection_service_browsertest.cc
index 0d6a2a3..2bd5487 100644
--- a/chrome/browser/safe_browsing/notification_content_detection/notification_content_detection_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/notification_content_detection/notification_content_detection_service_browsertest.cc
@@ -589,7 +589,8 @@
   std::unique_ptr<NotificationHandler> handler =
       std::make_unique<PersistentNotificationHandler>();
   handler->DisableNotifications(browser()->profile(), GURL(kNonAllowlistedUrl),
-                                /*notification_id=*/std::nullopt);
+                                /*notification_id=*/std::nullopt,
+                                /*is_suspicious=*/false);
   NotificationPermissionContext::UpdatePermission(
       browser()->profile(), GURL(kNonAllowlistedUrl), CONTENT_SETTING_ALLOW);
 
diff --git a/chrome/browser/save_to_drive/save_to_drive_event_dispatcher_browsertest.cc b/chrome/browser/save_to_drive/save_to_drive_event_dispatcher_browsertest.cc
index e28b586..7ceba2c 100644
--- a/chrome/browser/save_to_drive/save_to_drive_event_dispatcher_browsertest.cc
+++ b/chrome/browser/save_to_drive/save_to_drive_event_dispatcher_browsertest.cc
@@ -91,6 +91,14 @@
   }
 
   void TearDownOnMainThread() override {
+    // At the end of a Save to Drive upload test, the state in the UI needs to
+    // be reset, or else it will be blocked by the beforeunload dialog.
+    pdf_api::SaveToDriveProgress progress;
+    progress.status = pdf_api::SaveToDriveStatus::kNotStarted;
+    progress.error_type = pdf_api::SaveToDriveErrorType::kNoError;
+    EXPECT_CALL(*save_to_drive_recorder_, Record);
+    dispatcher_->Notify(std::move(progress));
+
     save_to_drive_recorder_ = nullptr;
     time_remaining_calculator_ = nullptr;
     dispatcher_.reset();
diff --git a/chrome/browser/sessions/session_restore_android.cc b/chrome/browser/sessions/session_restore_android.cc
index ee8b13f..b73812a 100644
--- a/chrome/browser/sessions/session_restore_android.cc
+++ b/chrome/browser/sessions/session_restore_android.cc
@@ -56,10 +56,15 @@
 
   TabAndroid* current_tab = TabAndroid::FromWebContents(web_contents);
   DCHECK(current_tab);
-  // If swapped, return the current tab's most up-to-date web contents.
+  // Fake replacing the current tab's WebContents with a new one by creating and
+  // selecting a new tab then closing the old one. Using the current tab as the
+  // parent will ensure group state, position, etc. should be kept.
   if (disposition == WindowOpenDisposition::CURRENT_TAB) {
-    current_tab->SwapWebContents(std::move(new_web_contents), false, false);
-    return current_tab->web_contents();
+    // This will never be a bulk session restore so we can select the tab here.
+    tab_model->CreateTab(current_tab, new_web_contents.release(),
+                         /*select=*/true);
+    tab_model->CloseTab(current_tab->GetHandle());
+    return raw_new_web_contents;
   }
   DCHECK(disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
          disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB);
diff --git a/chrome/browser/shortcuts/shortcut_creation_test_support_win.cc b/chrome/browser/shortcuts/shortcut_creation_test_support_win.cc
index 0b31ff63..39260ac 100644
--- a/chrome/browser/shortcuts/shortcut_creation_test_support_win.cc
+++ b/chrome/browser/shortcuts/shortcut_creation_test_support_win.cc
@@ -8,6 +8,7 @@
 
 #include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "base/strings/utf_ostream_operators.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/win/shortcut.h"
diff --git a/chrome/browser/storage_access_api/api_browsertest.cc b/chrome/browser/storage_access_api/api_browsertest.cc
index e996317..6a8ace4 100644
--- a/chrome/browser/storage_access_api/api_browsertest.cc
+++ b/chrome/browser/storage_access_api/api_browsertest.cc
@@ -792,8 +792,8 @@
       "Browser.setPermission",
       base::Value::Dict()
           .Set("setting", "granted")
-          .Set("origin", kOriginB)
-          .Set("embeddingOrigin", kOriginA)
+          .Set("origin", kOriginA)
+          .Set("embeddedOrigin", kOriginB)
           .Set("permission",
                base::Value::Dict().Set("name", "storage-access")));
   test_storage_access(/*expected_for_frame=*/true,
diff --git a/chrome/browser/sync/sync_service_factory.cc b/chrome/browser/sync/sync_service_factory.cc
index 9ca961ef..36e1222 100644
--- a/chrome/browser/sync/sync_service_factory.cc
+++ b/chrome/browser/sync/sync_service_factory.cc
@@ -14,6 +14,7 @@
 #include "base/no_destructor.h"
 #include "build/build_config.h"
 #include "chrome/browser/android/webapk/webapk_sync_service_factory.h"
+#include "chrome/browser/autofill/account_setting_service_factory.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browser_process.h"
@@ -187,6 +188,8 @@
 #endif  // DCHECK_IS_ON()
 
   browser_sync::CommonControllerBuilder builder;
+  builder.SetAccountSettingService(
+      autofill::AccountSettingServiceFactory::GetForBrowserContext(profile));
   // A callback is needed here because `autofill::PersonalDataManagerFactory`
   // already depends on `SyncServiceFactory`.
   builder.SetAddressDataManagerGetter(
@@ -512,6 +515,7 @@
   // destruction order. Note that some of the dependencies are listed here but
   // actually plumbed in ChromeSyncClient, which this factory constructs.
   DependsOn(AboutSigninInternalsFactory::GetInstance());
+  DependsOn(autofill::AccountSettingServiceFactory::GetInstance());
   DependsOn(AccountBookmarkSyncServiceFactory::GetInstance());
   DependsOn(AccountPasswordStoreFactory::GetInstance());
   DependsOn(BookmarkModelFactory::GetInstance());
diff --git a/chrome/browser/sync/test/integration/BUILD.gn b/chrome/browser/sync/test/integration/BUILD.gn
index f67d290b..953b5c3 100644
--- a/chrome/browser/sync/test/integration/BUILD.gn
+++ b/chrome/browser/sync/test/integration/BUILD.gn
@@ -18,6 +18,7 @@
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
   deps = [
     ":sync_integration_test_support",
+    "//chrome/browser/autofill",
     "//chrome/browser/history",
     "//chrome/browser/password_manager/factories",
     "//chrome/browser/plus_addresses",
@@ -30,6 +31,8 @@
     "//chrome/browser/ui:ui_features",
     "//chrome/test:browser_tests_runner",
     "//chrome/test:test_support",
+    "//components/autofill/core/browser",
+    "//components/autofill/core/browser:test_support",
     "//components/bookmarks/browser",
     "//components/commerce/core:feature_list",
     "//components/data_sharing/public",
@@ -50,6 +53,7 @@
   ]
 
   sources = [
+    "single_client_account_setting_sync_test.cc",
     "single_client_autofill_profile_sync_test.cc",
     "single_client_bookmarks_sync_test.cc",
     "single_client_collaboration_group_sync_test.cc",
diff --git a/chrome/browser/sync/test/integration/password_sharing_invitation_helper.cc b/chrome/browser/sync/test/integration/password_sharing_invitation_helper.cc
index 6ab45b1..1b0576c8 100644
--- a/chrome/browser/sync/test/integration/password_sharing_invitation_helper.cc
+++ b/chrome/browser/sync/test/integration/password_sharing_invitation_helper.cc
@@ -45,12 +45,9 @@
       syncer::CryptographerImpl::CreateEmpty();
 
   // Clone `key_pair` since the cryptographer requires it to be moved.
-  std::optional<syncer::CrossUserSharingPublicPrivateKeyPair> key_pair_copy =
-      syncer::CrossUserSharingPublicPrivateKeyPair::CreateByImport(
-          key_pair.GetRawPrivateKey());
-  CHECK(key_pair_copy);
-  cryptographer->SetKeyPair(std::move(key_pair_copy.value()),
-                            kDefaultKeyVersion);
+  cryptographer->SetKeyPair(
+      syncer::CrossUserSharingPublicPrivateKeyPair(key_pair.GetRawPrivateKey()),
+      kDefaultKeyVersion);
   cryptographer->SelectDefaultCrossUserSharingKey(kDefaultKeyVersion);
 
   return cryptographer;
diff --git a/chrome/browser/sync/test/integration/single_client_account_setting_sync_test.cc b/chrome/browser/sync/test/integration/single_client_account_setting_sync_test.cc
new file mode 100644
index 0000000..d6bad08
--- /dev/null
+++ b/chrome/browser/sync/test/integration/single_client_account_setting_sync_test.cc
@@ -0,0 +1,182 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "chrome/browser/autofill/account_setting_service_factory.h"
+#include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
+#include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
+#include "chrome/browser/sync/test/integration/sync_test.h"
+#include "components/autofill/core/browser/webdata/account_settings/account_setting_service.h"
+#include "components/autofill/core/browser/webdata/account_settings/account_setting_sync_util.h"
+#include "components/sync/base/client_tag_hash.h"
+#include "components/sync/base/data_type.h"
+#include "components/sync/base/features.h"
+#include "components/sync/engine/loopback_server/persistent_unique_client_entity.h"
+#include "components/sync/protocol/account_setting_specifics.pb.h"
+#include "components/sync/protocol/entity_specifics.pb.h"
+#include "components/sync/test/fake_server.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+constexpr std::string_view kWalletPrivacyContextualSurfacingSetting =
+    "WALLET_PRIVACY_CONTEXTUAL_SURFACING";
+
+using autofill::AccountSettingService;
+using autofill::CreateSettingSpecifics;
+
+// Waits until
+// `AccountSettingService::IsWalletPrivacyContextualSurfacingEnabled()` has the
+// `expected_state`.
+// The condition is checked whenever sync's status changes - in particular, each
+// time a sync cycle completes. This works, since setting changes are exposed
+// through AccountSettingService synchronously after the operation on the bridge
+// completes.
+class WalletSurfacingChecker : public SingleClientStatusChangeChecker {
+ public:
+  WalletSurfacingChecker(syncer::SyncServiceImpl* sync_service,
+                         AccountSettingService* setting_service,
+                         bool expected_state)
+      : SingleClientStatusChangeChecker(sync_service),
+        setting_service_(setting_service),
+        expected_state_(expected_state) {}
+
+  // SingleClientStatusChangeChecker:
+  bool IsExitConditionSatisfied(std::ostream* os) override {
+    return setting_service_->IsWalletPrivacyContextualSurfacingEnabled() ==
+           expected_state_;
+  }
+
+ private:
+  const raw_ptr<AccountSettingService> setting_service_;
+  const bool expected_state_;
+};
+
+// ACCOUNT_SETTING is supposed to behave the same in and outside of transport
+// mode. These tests are parameterized by whether the test should run in
+// transport mode (true) or not (false).
+class SingleClientAccountSettingSyncTest
+    : public SyncTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  SingleClientAccountSettingSyncTest() : SyncTest(SINGLE_CLIENT) {
+    features_.InitWithFeatures(
+        /*enabled_features=*/{syncer::kSyncAccountSettings},
+        /*disabled_features=*/{});
+  }
+
+  // Sets up the sync client in sync-the-feature or sync-the-transport mode,
+  // depending on `GetParam()`. Returns true if setup succeeded.
+  bool SetupSync() {
+    const bool should_run_in_transport_mode = GetParam();
+    if (should_run_in_transport_mode) {
+      return SetupClients() && GetClient(0)->SignInPrimaryAccount() &&
+             GetClient(0)->AwaitSyncTransportActive();
+    }
+    return SyncTest::SetupSync();
+  }
+
+  AccountSettingService* GetAccountSettingService() {
+    return autofill::AccountSettingServiceFactory::GetForBrowserContext(
+        GetProfile(0));
+  }
+
+  void InjectSpecificsToServer(
+      const sync_pb::AccountSettingSpecifics& specifics) {
+    sync_pb::EntitySpecifics entity_specifics;
+    *entity_specifics.mutable_account_setting() = specifics;
+    GetFakeServer()->InjectEntity(
+        syncer::PersistentUniqueClientEntity::CreateFromSpecificsForTesting(
+            /*non_unique_name=*/"account-setting",
+            /*client_tag=*/
+            specifics.name(), entity_specifics,
+            /*creation_time=*/0, /*last_modified_time=*/0));
+  }
+
+  bool WaitForWalletSurfacingState(bool expected_state) {
+    return WalletSurfacingChecker(GetClient(0)->service(),
+                                  GetAccountSettingService(), expected_state)
+        .Wait();
+  }
+
+ private:
+  base::test::ScopedFeatureList features_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    SingleClientAccountSettingSyncTest,
+#if BUILDFLAG(IS_CHROMEOS)
+    // On ChromeOS, sync-the-feature gets started automatically once a primary
+    // account is signed in and transport mode is not a thing. As such, only run
+    // the tests in sync-the-feature mode.
+    testing::Values(false)
+#else
+    testing::Bool()
+#endif
+);
+
+IN_PROC_BROWSER_TEST_P(SingleClientAccountSettingSyncTest, InitialSync) {
+  InjectSpecificsToServer(
+      CreateSettingSpecifics(kWalletPrivacyContextualSurfacingSetting, true));
+  ASSERT_TRUE(SetupSync());
+  EXPECT_TRUE(WaitForWalletSurfacingState(true));
+}
+
+IN_PROC_BROWSER_TEST_P(SingleClientAccountSettingSyncTest,
+                       IncrementalUpdate_Add) {
+  ASSERT_TRUE(SetupSync());
+  InjectSpecificsToServer(
+      CreateSettingSpecifics(kWalletPrivacyContextualSurfacingSetting, true));
+  EXPECT_TRUE(WaitForWalletSurfacingState(true));
+}
+
+IN_PROC_BROWSER_TEST_P(SingleClientAccountSettingSyncTest,
+                       IncrementalUpdate_Update) {
+  InjectSpecificsToServer(
+      CreateSettingSpecifics(kWalletPrivacyContextualSurfacingSetting, true));
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(WaitForWalletSurfacingState(true));
+  InjectSpecificsToServer(
+      CreateSettingSpecifics(kWalletPrivacyContextualSurfacingSetting, false));
+  EXPECT_TRUE(WaitForWalletSurfacingState(false));
+}
+
+IN_PROC_BROWSER_TEST_P(SingleClientAccountSettingSyncTest,
+                       IncrementalUpdate_Remove) {
+  InjectSpecificsToServer(
+      CreateSettingSpecifics(kWalletPrivacyContextualSurfacingSetting, true));
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(WaitForWalletSurfacingState(true));
+  // Simulate removing the `kIsEnabledSettingName` setting on the server.
+  const std::string client_tag_hash =
+      syncer::ClientTagHash::FromUnhashed(
+          syncer::ACCOUNT_SETTING, kWalletPrivacyContextualSurfacingSetting)
+          .value();
+  GetFakeServer()->InjectEntity(
+      syncer::PersistentTombstoneEntity::PersistentTombstoneEntity::CreateNew(
+          syncer::LoopbackServerEntity::CreateId(syncer::ACCOUNT_SETTING,
+                                                 client_tag_hash),
+          client_tag_hash));
+  // Non-existing settings behave as if they have their default value.
+  EXPECT_TRUE(WaitForWalletSurfacingState(false));
+}
+
+// ChromeOS does not support signing out of the primary account.
+#if !BUILDFLAG(IS_CHROMEOS)
+IN_PROC_BROWSER_TEST_P(SingleClientAccountSettingSyncTest,
+                       Signout_DataCleared) {
+  InjectSpecificsToServer(
+      CreateSettingSpecifics(kWalletPrivacyContextualSurfacingSetting, true));
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(WaitForWalletSurfacingState(true));
+  GetClient(0)->SignOutPrimaryAccount();
+  EXPECT_TRUE(WaitForWalletSurfacingState(false));
+}
+#endif  // !BUILDFLAG(IS_CHROMEOS)
+
+}  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
index c3288bb..2c0a813 100644
--- a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
@@ -876,12 +876,13 @@
   EXPECT_EQ(decrypted_keys.cross_user_sharing_private_key().at(0).version(), 0);
   std::vector<uint8_t> raw_private_key(private_key_proto.begin(),
                                        private_key_proto.end());
-  std::optional<syncer::CrossUserSharingPublicPrivateKeyPair> private_key =
-      syncer::CrossUserSharingPublicPrivateKeyPair::CreateByImport(
-          raw_private_key);
-  EXPECT_TRUE(private_key.has_value());
+  std::optional<base::span<uint8_t, X25519_PRIVATE_KEY_LEN>> fixed_private_key =
+      base::span(raw_private_key).to_fixed_extent<X25519_PRIVATE_KEY_LEN>();
+  ASSERT_TRUE(fixed_private_key);
+
+  syncer::CrossUserSharingPublicPrivateKeyPair private_key(*fixed_private_key);
   EXPECT_THAT(specifics.cross_user_sharing_public_key().x25519_public_key(),
-              testing::ElementsAreArray(private_key->GetRawPublicKey()));
+              testing::ElementsAreArray(private_key.GetRawPublicKey()));
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -984,12 +985,13 @@
   EXPECT_EQ(decrypted_keys.cross_user_sharing_private_key().at(0).version(), 0);
   std::vector<uint8_t> raw_private_key(private_key_proto.begin(),
                                        private_key_proto.end());
-  std::optional<syncer::CrossUserSharingPublicPrivateKeyPair> private_key =
-      syncer::CrossUserSharingPublicPrivateKeyPair::CreateByImport(
-          raw_private_key);
-  EXPECT_TRUE(private_key.has_value());
+  std::optional<base::span<uint8_t, X25519_PRIVATE_KEY_LEN>> fixed_private_key =
+      base::span(raw_private_key).to_fixed_extent<X25519_PRIVATE_KEY_LEN>();
+  ASSERT_TRUE(fixed_private_key);
+
+  syncer::CrossUserSharingPublicPrivateKeyPair private_key(*fixed_private_key);
   EXPECT_THAT(specifics.cross_user_sharing_public_key().x25519_public_key(),
-              testing::ElementsAreArray(private_key->GetRawPublicKey()));
+              testing::ElementsAreArray(private_key.GetRawPublicKey()));
 }
 
 IN_PROC_BROWSER_TEST_F(
diff --git a/chrome/browser/sync/test/integration/single_client_valuables_sync_test.cc b/chrome/browser/sync/test/integration/single_client_valuables_sync_test.cc
index 1bf127ca..520da2c 100644
--- a/chrome/browser/sync/test/integration/single_client_valuables_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_valuables_sync_test.cc
@@ -4,14 +4,17 @@
 
 #include <vector>
 
+#include "base/test/protobuf_matchers.h"
 #include "chrome/browser/autofill/autofill_entity_data_manager_factory.h"
 #include "chrome/browser/autofill/valuables_data_manager_factory.h"
+#include "chrome/browser/sync/test/integration/fake_server_match_status_checker.h"
 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
 #include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "components/autofill/core/browser/data_manager/autofill_ai/entity_data_manager_test_utils.h"
 #include "components/autofill/core/browser/data_manager/valuables/valuables_data_manager.h"
 #include "components/autofill/core/browser/data_manager/valuables/valuables_data_manager_test_utils.h"
+#include "components/autofill/core/browser/data_model/autofill_ai/entity_type_names.h"
 #include "components/autofill/core/browser/data_model/valuables/loyalty_card.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
 #include "components/autofill/core/browser/test_utils/valuables_data_test_utils.h"
@@ -39,6 +42,17 @@
 
 namespace {
 
+EntityInstance GetServerVehicleEntityInstance(
+    autofill::test::VehicleOptions options = {}) {
+  options.nickname = "";
+  options.date_modified = {};
+  options.use_date = {};
+  options.record_type = EntityInstance::RecordType::kServerWallet;
+  options.are_attributes_read_only =
+      EntityInstance::AreAttributesReadOnly(false);
+  return autofill::test::GetVehicleEntityInstance(options);
+}
+
 EntityInstance GetServerVehicleEntityInstanceWithRandomGuid() {
   return autofill::test::GetVehicleEntityInstanceWithRandomGuid(
       {.nickname = "",
@@ -99,6 +113,44 @@
   return entity;
 }
 
+// Since the sync server operates in terms of entity specifics, this helper
+// function converts a given `entity_instance` to the equivalent
+// `AutofillValuableSpecifics`.
+sync_pb::AutofillValuableSpecifics AsAutofillValuableSpecifics(
+    const EntityInstance& entity_instance) {
+  return autofill::CreateSpecificsFromEntityInstance(entity_instance);
+}
+
+// Helper class to wait until the fake server's AutofillValuableSpecifics match
+// a given predicate. Unfortunately, since protos don't have an equality
+// operator, the comparisons are based on the `base::test::EqualsProto()`
+// representation of the specifics.
+class FakeServerSpecificsChecker
+    : public fake_server::FakeServerMatchStatusChecker {
+ public:
+  using Matcher =
+      testing::Matcher<std::vector<sync_pb::AutofillValuableSpecifics>>;
+
+  explicit FakeServerSpecificsChecker(const Matcher& matcher)
+      : matcher_(matcher) {}
+
+  // StatusChangeChecker implementation.
+  bool IsExitConditionSatisfied(std::ostream* os) override {
+    std::vector<sync_pb::AutofillValuableSpecifics> specifics;
+    for (const sync_pb::SyncEntity& entity :
+         fake_server()->GetSyncEntitiesByDataType(syncer::AUTOFILL_VALUABLE)) {
+      specifics.push_back(entity.specifics().autofill_valuable());
+    }
+    testing::StringMatchResultListener listener;
+    bool matches = testing::ExplainMatchResult(matcher_, specifics, &listener);
+    *os << listener.str();
+    return matches;
+  }
+
+ private:
+  const Matcher matcher_;
+};
+
 class SingleClientValuableSyncTestBase : public SyncTest {
  public:
   SingleClientValuableSyncTestBase() : SyncTest(SINGLE_CLIENT) {}
@@ -471,4 +523,63 @@
   EXPECT_EQ(0uL, edm->GetEntityInstances().size());
 }
 
+// Verifies that local entities are never uploaded to the sync server.
+IN_PROC_BROWSER_TEST_F(SingleClientEntityValuablesSyncTest,
+                       NotUploadLocalEntity) {
+  ASSERT_TRUE(SetupSync());
+  EntityDataManager* edm = GetEntityDataManager(0);
+  ASSERT_NE(nullptr, edm);
+  ASSERT_THAT(edm->GetEntityInstances(), testing::IsEmpty());
+  const EntityInstance vehicle =
+      autofill::test::GetVehicleEntityInstanceWithRandomGuid();
+  edm->AddOrUpdateEntityInstance(vehicle);
+  EXPECT_TRUE(FakeServerSpecificsChecker(testing::IsEmpty()).Wait());
+}
+
+// Verifies that a new wallet entity created locally is successfully uploaded to
+// the sync server.
+IN_PROC_BROWSER_TEST_F(SingleClientEntityValuablesSyncTest,
+                       UploadWalletEntity) {
+  ASSERT_TRUE(SetupSync());
+  EntityDataManager* edm = GetEntityDataManager(0);
+  ASSERT_NE(nullptr, edm);
+  ASSERT_THAT(edm->GetEntityInstances(), testing::IsEmpty());
+  const EntityInstance vehicle = GetServerVehicleEntityInstanceWithRandomGuid();
+  edm->AddOrUpdateEntityInstance(vehicle);
+  EXPECT_TRUE(
+      FakeServerSpecificsChecker(UnorderedElementsAre(base::test::EqualsProto(
+                                     AsAutofillValuableSpecifics(vehicle))))
+          .Wait());
+
+  const EntityInstance vehicle2 =
+      GetServerVehicleEntityInstanceWithRandomGuid();
+  edm->AddOrUpdateEntityInstance(vehicle2);
+  EXPECT_TRUE(
+      FakeServerSpecificsChecker(
+          UnorderedElementsAre(
+              base::test::EqualsProto(AsAutofillValuableSpecifics(vehicle)),
+              base::test::EqualsProto(AsAutofillValuableSpecifics(vehicle2))))
+          .Wait());
+}
+
+// Verifies that updating an existing wallet entity locally correctly propagates
+// that update to the sync server.
+IN_PROC_BROWSER_TEST_F(SingleClientEntityValuablesSyncTest,
+                       UploadAndUpdateWalletEntity) {
+  ASSERT_TRUE(SetupSync());
+  EntityDataManager* edm = GetEntityDataManager(0);
+  ASSERT_NE(nullptr, edm);
+  ASSERT_THAT(edm->GetEntityInstances(), testing::IsEmpty());
+  const EntityInstance vehicle = GetServerVehicleEntityInstance();
+  edm->AddOrUpdateEntityInstance(vehicle);
+  const EntityInstance updated_vehicle =
+      GetServerVehicleEntityInstance({.model = u"Q2"});
+  // Update vehicle
+  edm->AddOrUpdateEntityInstance(updated_vehicle);
+  EXPECT_TRUE(FakeServerSpecificsChecker(
+                  UnorderedElementsAre(base::test::EqualsProto(
+                      AsAutofillValuableSpecifics(updated_vehicle))))
+                  .Wait());
+}
+
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/wallet_helper.cc b/chrome/browser/sync/test/integration/wallet_helper.cc
index 0a66806b..8addb3a 100644
--- a/chrome/browser/sync/test/integration/wallet_helper.cc
+++ b/chrome/browser/sync/test/integration/wallet_helper.cc
@@ -629,3 +629,8 @@
     syncer::SyncService* sync) {
   CheckExitCondition();
 }
+
+void FullUpdateTypeProgressMarkerChecker::OnSyncShutdown(
+    syncer::SyncService* sync) {
+  NOTREACHED();
+}
diff --git a/chrome/browser/sync/test/integration/wallet_helper.h b/chrome/browser/sync/test/integration/wallet_helper.h
index 7113295..ab736b7a 100644
--- a/chrome/browser/sync/test/integration/wallet_helper.h
+++ b/chrome/browser/sync/test/integration/wallet_helper.h
@@ -191,6 +191,7 @@
 
   // syncer::SyncServiceObserver:
   void OnSyncCycleCompleted(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
  private:
   const base::Time min_required_progress_marker_timestamp_;
diff --git a/chrome/browser/tab/BUILD.gn b/chrome/browser/tab/BUILD.gn
index 5f49ed4..0abe526 100644
--- a/chrome/browser/tab/BUILD.gn
+++ b/chrome/browser/tab/BUILD.gn
@@ -191,7 +191,9 @@
 source_set("tab") {
   sources = [
     "android_tab_package.h",
+    "collection_storage_observer.h",
     "collection_storage_package.h",
+    "collection_structure_tracker.h",
     "storage_package.h",
     "tab_state_storage_backend.h",
     "tab_state_storage_database.h",
@@ -215,7 +217,9 @@
 source_set("impl") {
   sources = [
     "android_tab_package.cc",
+    "collection_storage_observer.cc",
     "collection_storage_package.cc",
+    "collection_structure_tracker.cc",
     "tab_state_storage_backend.cc",
     "tab_state_storage_database.cc",
     "tab_state_storage_service.cc",
@@ -234,17 +238,3 @@
     "//sql",
   ]
 }
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [ "web_contents_state_unittest.cc" ]
-  deps = [
-    ":tab",
-    "//base",
-    "//components/sessions",
-    "//components/tabs:public",
-    "//content/test:test_support",
-    "//testing/gtest",
-    "//ui/base",
-  ]
-}
diff --git a/chrome/browser/tab/collection_storage_observer.cc b/chrome/browser/tab/collection_storage_observer.cc
new file mode 100644
index 0000000..a90d2ed
--- /dev/null
+++ b/chrome/browser/tab/collection_storage_observer.cc
@@ -0,0 +1,28 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/tab/collection_storage_observer.h"
+
+#include "chrome/browser/tab/tab_state_storage_service.h"
+
+namespace tabs {
+
+CollectionStorageObserver::CollectionStorageObserver(
+    tabs::TabStateStorageService* service)
+    : service_(service) {}
+
+CollectionStorageObserver::~CollectionStorageObserver() = default;
+
+void CollectionStorageObserver::OnChildrenAdded(
+    const Position& position,
+    const tabs::TabCollectionNodes& handles) {}
+
+void CollectionStorageObserver::OnChildrenRemoved(
+    const tabs::TabCollectionNodes& handles) {}
+
+void CollectionStorageObserver::OnChildrenMoved(
+    const Position& position,
+    const tabs::TabCollectionNodes& handles) {}
+
+}  // namespace tabs
diff --git a/chrome/browser/tab/collection_storage_observer.h b/chrome/browser/tab/collection_storage_observer.h
new file mode 100644
index 0000000..f9b64895
--- /dev/null
+++ b/chrome/browser/tab/collection_storage_observer.h
@@ -0,0 +1,39 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_TAB_COLLECTION_STORAGE_OBSERVER_H_
+#define CHROME_BROWSER_TAB_COLLECTION_STORAGE_OBSERVER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "chrome/browser/tab/tab_state_storage_service.h"
+#include "components/tabs/public/tab_collection_observer.h"
+
+namespace tabs {
+
+// Observes changes to the structure of a TabStripCollection.
+// This class does not manage observer registration and
+// unregistration.
+class CollectionStorageObserver : public TabCollectionObserver {
+ public:
+  explicit CollectionStorageObserver(TabStateStorageService* service);
+  ~CollectionStorageObserver() override;
+
+  CollectionStorageObserver(const CollectionStorageObserver&) = delete;
+  CollectionStorageObserver& operator=(const CollectionStorageObserver&) =
+      delete;
+
+  // TabCollectionObserver Implementation:
+  void OnChildrenAdded(const Position& position,
+                       const TabCollectionNodes& handles) override;
+  void OnChildrenRemoved(const TabCollectionNodes& handles) override;
+  void OnChildrenMoved(const Position& position,
+                       const TabCollectionNodes& handles) override;
+
+ private:
+  raw_ptr<TabStateStorageService> service_;
+};
+
+}  // namespace tabs
+
+#endif  // CHROME_BROWSER_TAB_COLLECTION_STORAGE_OBSERVER_H_
diff --git a/chrome/browser/tab/collection_structure_tracker.cc b/chrome/browser/tab/collection_structure_tracker.cc
new file mode 100644
index 0000000..0e045945
--- /dev/null
+++ b/chrome/browser/tab/collection_structure_tracker.cc
@@ -0,0 +1,26 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/tab/collection_structure_tracker.h"
+
+#include <memory>
+
+#include "chrome/browser/tab/collection_storage_observer.h"
+#include "components/tabs/public/tab_strip_collection.h"
+
+namespace tabs {
+
+CollectionStructureTracker::CollectionStructureTracker(
+    TabStripCollection* collection,
+    TabStateStorageService* service)
+    : collection_(collection) {
+  observer_ = std::make_unique<CollectionStorageObserver>(service);
+  collection_->AddObserver(observer_.get());
+}
+
+CollectionStructureTracker::~CollectionStructureTracker() {
+  collection_->RemoveObserver(observer_.get());
+}
+
+}  // namespace tabs
diff --git a/chrome/browser/tab/collection_structure_tracker.h b/chrome/browser/tab/collection_structure_tracker.h
new file mode 100644
index 0000000..0d0a054
--- /dev/null
+++ b/chrome/browser/tab/collection_structure_tracker.h
@@ -0,0 +1,35 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_TAB_COLLECTION_STRUCTURE_TRACKER_H_
+#define CHROME_BROWSER_TAB_COLLECTION_STRUCTURE_TRACKER_H_
+
+#include <memory>
+
+#include "base/memory/raw_ptr.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/tab/collection_storage_observer.h"
+#include "chrome/browser/tab/tab_state_storage_service.h"
+#include "components/tabs/public/tab_strip_collection.h"
+namespace tabs {
+
+// Provides updates to storage to match the state of a TabStripCollection.
+class CollectionStructureTracker {
+ public:
+  CollectionStructureTracker(TabStripCollection* collection,
+                             TabStateStorageService* service);
+  ~CollectionStructureTracker();
+
+  CollectionStructureTracker(const CollectionStructureTracker&) = delete;
+  CollectionStructureTracker& operator=(const CollectionStructureTracker&) =
+      delete;
+
+ private:
+  raw_ptr<TabStripCollection> collection_;
+  std::unique_ptr<CollectionStorageObserver> observer_;
+};
+
+}  // namespace tabs
+
+#endif  // CHROME_BROWSER_TAB_COLLECTION_STRUCTURE_TRACKER_H_
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObserver.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObserver.java
index fe4aea06..c7e00f2 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObserver.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObserver.java
@@ -164,7 +164,9 @@
     /**
      * Called when the WebContents of a {@link Tab} is about to be swapped.
      * @param tab The notifying {@link Tab}
+     * @deprecated This method will be removed soon as swapping web contents will no longer be possible.
      */
+    @Deprecated
     void webContentsWillSwap(Tab tab);
 
     /**
@@ -173,7 +175,9 @@
      * @param didStartLoad Whether WebContentsObserver::DidStartProvisionalLoadForFrame() has
      *     already been called.
      * @param didFinishLoad Whether WebContentsObserver::DidFinishLoad() has already been called.
+     * @deprecated This method will be removed soon as swapping web contents will no longer be possible.
      */
+    @Deprecated
     void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad);
 
     /**
diff --git a/chrome/browser/tab/tab_state_storage_service.cc b/chrome/browser/tab/tab_state_storage_service.cc
index b268b780..4984c1ae 100644
--- a/chrome/browser/tab/tab_state_storage_service.cc
+++ b/chrome/browser/tab/tab_state_storage_service.cc
@@ -10,10 +10,27 @@
 #include "chrome/browser/tab/protocol/tab_state.pb.h"
 #include "chrome/browser/tab/storage_package.h"
 #include "chrome/browser/tab/tab_storage_packager.h"
+#include "components/tabs/public/tab_collection.h"
 #include "components/tabs/public/tab_interface.h"
 
 namespace tabs {
 
+namespace {
+
+template <typename T>
+int GetOrCreateStorageId(T* object,
+                         absl::flat_hash_map<int32_t, int>& handle_map,
+                         int& next_storage_id) {
+  int32_t handle_id = object->GetHandle().raw_value();
+  auto [it, inserted] = handle_map.try_emplace(handle_id, next_storage_id);
+  if (inserted) {
+    next_storage_id++;
+  }
+  return it->second;
+}
+
+}  // namespace
+
 TabStateStorageService::TabStateStorageService(
     std::unique_ptr<TabStateStorageBackend> tab_backend,
     std::unique_ptr<TabStoragePackager> packager)
@@ -31,7 +48,10 @@
   packager_->Package(tab);
   std::unique_ptr<StoragePackage> package = packager_->ReleasePackage();
   DCHECK(package) << "Packager should return a package";
-  // TODO(https://crbug.com/448875689): Save through backend when we have an id.
+
+  int storage_id = GetOrCreateStorageId(tab);
+  // TODO(https://crbug.com/448875689): Create a type enum.
+  tab_backend_->Save(storage_id, 1, std::move(package));
 }
 
 void TabStateStorageService::Save(const TabCollection* collection) {
@@ -42,7 +62,10 @@
   packager_->Package(collection);
   std::unique_ptr<StoragePackage> package = packager_->ReleasePackage();
   DCHECK(package) << "Packager should return a package";
-  // TODO(https://crbug.com/448875689): Save through backend when we have an id.
+
+  int storage_id = GetOrCreateStorageId(collection);
+  // TODO(https://crbug.com/448875689): Create a type enum.
+  tab_backend_->Save(storage_id, 2, std::move(package));
 }
 
 void TabStateStorageService::LoadAllTabs(LoadAllTabsCallback callback) {
@@ -54,7 +77,9 @@
 void TabStateStorageService::OnAllTabsLoaded(LoadAllTabsCallback callback,
                                              std::vector<NodeState> entries) {
   std::vector<tabs_pb::TabState> tab_states;
+  int max_storage_id = 0;
   for (auto& entry : entries) {
+    max_storage_id = std::max(max_storage_id, entry.id);
     if (entry.type == 1) {
       tabs_pb::TabState tab_state;
       if (tab_state.ParseFromString(entry.payload)) {
@@ -62,7 +87,19 @@
       }
     }
   }
+  next_storage_id_ = max_storage_id + 1;
   std::move(callback).Run(std::move(tab_states));
 }
 
+int TabStateStorageService::GetOrCreateStorageId(
+    const TabCollection* collection) {
+  return ::tabs::GetOrCreateStorageId(
+      collection, collection_handle_to_storage_id_, next_storage_id_);
+}
+
+int TabStateStorageService::GetOrCreateStorageId(const TabInterface* tab) {
+  return ::tabs::GetOrCreateStorageId(tab, tab_handle_to_storage_id_,
+                                      next_storage_id_);
+}
+
 }  // namespace tabs
diff --git a/chrome/browser/tab/tab_state_storage_service.h b/chrome/browser/tab/tab_state_storage_service.h
index 2627087..3ab8ef3 100644
--- a/chrome/browser/tab/tab_state_storage_service.h
+++ b/chrome/browser/tab/tab_state_storage_service.h
@@ -18,6 +18,7 @@
 #include "chrome/browser/tab/tab_storage_packager.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/tabs/public/tab_interface.h"
+#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
 
 namespace tabs_pb {
 class TabState;
@@ -49,8 +50,18 @@
  private:
   void OnAllTabsLoaded(LoadAllTabsCallback callback,
                        std::vector<NodeState> entries);
+
+  int GetOrCreateStorageId(const TabCollection* collection);
+  int GetOrCreateStorageId(const TabInterface* tab);
+
   std::unique_ptr<TabStateStorageBackend> tab_backend_;
   std::unique_ptr<TabStoragePackager> packager_;
+
+  // Storage ids need to be unique across tabs and collections, but the handles
+  // do not have this guarantee. Track them separately.
+  int next_storage_id_ = 1;
+  absl::flat_hash_map<int32_t, int> tab_handle_to_storage_id_;
+  absl::flat_hash_map<int32_t, int> collection_handle_to_storage_id_;
 };
 
 }  // namespace tabs
diff --git a/chrome/browser/tab/web_contents_state.cc b/chrome/browser/tab/web_contents_state.cc
index 1281988..294899f 100644
--- a/chrome/browser/tab/web_contents_state.cc
+++ b/chrome/browser/tab/web_contents_state.cc
@@ -188,20 +188,6 @@
 WebContentsStateByteBuffer::WebContentsStateByteBuffer(
     WebContentsStateByteBuffer&& other) noexcept = default;
 
-WebContentsStateUnpacked::WebContentsStateUnpacked(
-    bool is_off_the_record,
-    int current_entry_index,
-    std::vector<sessions::SerializedNavigationEntry> navigations)
-    : is_off_the_record_(is_off_the_record),
-      current_entry_index_(current_entry_index),
-      navigations_(std::move(navigations)) {}
-WebContentsStateUnpacked::~WebContentsStateUnpacked() = default;
-
-ScopedJavaLocalRef<jobject> WebContentsStateUnpacked::Pack(JNIEnv* env) const {
-  return WriteSerializedNavigationsAsByteBuffer(
-      env, is_off_the_record_, navigations_, current_entry_index_);
-}
-
 ScopedJavaLocalRef<jobject> WebContentsState::GetContentsStateAsByteBuffer(
     JNIEnv* env,
     content::WebContents* web_contents) {
@@ -228,23 +214,6 @@
       controller.GetLastCommittedEntryIndex());
 }
 
-std::unique_ptr<WebContentsStateUnpacked> WebContentsState::Unpack(
-    base::span<const uint8_t> buffer,
-    int saved_state_version) {
-  bool is_off_the_record;
-  int current_entry_index;
-  std::vector<sessions::SerializedNavigationEntry> navigations;
-  bool success = WebContentsState::ExtractNavigationEntries(
-      buffer, saved_state_version, &is_off_the_record, &current_entry_index,
-      &navigations);
-  if (!success) {
-    return nullptr;
-  }
-
-  return std::make_unique<WebContentsStateUnpacked>(
-      is_off_the_record, current_entry_index, std::move(navigations));
-}
-
 ScopedJavaLocalRef<jobject>
 WebContentsState::DeleteNavigationEntriesFromByteBuffer(
     JNIEnv* env,
diff --git a/chrome/browser/tab/web_contents_state.h b/chrome/browser/tab/web_contents_state.h
index 29bb9d5..884fb51 100644
--- a/chrome/browser/tab/web_contents_state.h
+++ b/chrome/browser/tab/web_contents_state.h
@@ -67,32 +67,6 @@
   base::android::ScopedJavaGlobalRef<jobject> java_buffer;
 };
 
-// A class to store unpacked version of WebContentsStateByteBuffer.
-class WebContentsStateUnpacked {
- public:
-  WebContentsStateUnpacked(
-      bool is_off_the_record,
-      int current_entry_index,
-      std::vector<sessions::SerializedNavigationEntry> navigations);
-  ~WebContentsStateUnpacked();
-
-  WebContentsStateUnpacked(const WebContentsStateUnpacked&) = delete;
-  WebContentsStateUnpacked& operator=(const WebContentsStateUnpacked&) = delete;
-
-  bool is_off_the_record() const { return is_off_the_record_; }
-  const std::vector<sessions::SerializedNavigationEntry>& navigations() const {
-    return navigations_;
-  }
-  int current_entry_index() const { return current_entry_index_; }
-
-  base::android::ScopedJavaLocalRef<jobject> Pack(JNIEnv* env) const;
-
- private:
-  bool is_off_the_record_;
-  int current_entry_index_;
-  std::vector<sessions::SerializedNavigationEntry> navigations_;
-};
-
 // Stores state for a WebContents, including its navigation history.
 class WebContentsState {
  public:
@@ -102,12 +76,6 @@
   static base::android::ScopedJavaLocalRef<jobject>
   GetContentsStateAsByteBuffer(JNIEnv* env, content::WebContents* web_contents);
 
-  // Unpacks `buffer` into a WebContentsStateUnpacked. Returns nullptr if the
-  // buffer is invalid.
-  static std::unique_ptr<WebContentsStateUnpacked> Unpack(
-      base::span<const uint8_t> buffer,
-      int saved_state_version);
-
   // Returns a new buffer without the navigations matching |predicate|.
   // Returns null if no deletions happened.
   static base::android::ScopedJavaLocalRef<jobject>
diff --git a/chrome/browser/tab/web_contents_state_unittest.cc b/chrome/browser/tab/web_contents_state_unittest.cc
deleted file mode 100644
index 2885ab40..0000000
--- a/chrome/browser/tab/web_contents_state_unittest.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/tab/web_contents_state.h"
-
-#include "base/android/jni_android.h"
-#include "base/android/jni_bytebuffer.h"
-#include "base/logging.h"
-#include "base/time/time.h"
-#include "components/sessions/core/serialized_navigation_entry.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/page_transition_types.h"
-#include "url/gurl.h"
-
-namespace {
-
-sessions::SerializedNavigationEntry CreateNavigation(
-    int index,
-    const std::string& url,
-    const std::u16string& title) {
-  sessions::SerializedNavigationEntry entry;
-  entry.set_index(index);
-  entry.set_virtual_url(GURL(url));
-  entry.set_title(title);
-  entry.set_transition_type(ui::PAGE_TRANSITION_LINK);
-  return entry;
-}
-
-void ExpectNavigationsEqual(const sessions::SerializedNavigationEntry& expected,
-                            const sessions::SerializedNavigationEntry& actual) {
-  EXPECT_EQ(expected.index(), actual.index());
-  EXPECT_EQ(expected.virtual_url(), actual.virtual_url());
-  EXPECT_EQ(expected.title(), actual.title());
-}
-
-size_t CalculateUnpackedSize(const WebContentsStateUnpacked& unpacked) {
-  size_t size = sizeof(WebContentsStateUnpacked);
-  size += unpacked.navigations().capacity() *
-          sizeof(sessions::SerializedNavigationEntry);
-  for (const auto& nav : unpacked.navigations()) {
-    size += nav.virtual_url().spec().size();
-    size += nav.title().size() * sizeof(char16_t);
-    size += nav.encoded_page_state().size();
-  }
-  return size;
-}
-
-}  // namespace
-
-class WebContentsStateUnpackedTest : public ::testing::Test {
- public:
-  WebContentsStateUnpackedTest() = default;
-
- private:
-  content::BrowserTaskEnvironment task_environment_;
-};
-
-TEST_F(WebContentsStateUnpackedTest, PackAndUnpack) {
-  const bool kIsOffTheRecord = false;
-  const int kCurrentEntryIndex = 1;
-  const int kSavedStateVersion = 2;
-
-  std::vector<sessions::SerializedNavigationEntry> navigations;
-  navigations.push_back(
-      CreateNavigation(0, "https://example.com/0", u"Title 0"));
-  navigations.push_back(
-      CreateNavigation(1, "https://example.com/1", u"Title 1"));
-  navigations.push_back(
-      CreateNavigation(2, "https://example.com/2", u"Title 2"));
-  const auto navigations_for_comparison = navigations;
-
-  WebContentsStateUnpacked original_unpacked(
-      kIsOffTheRecord, kCurrentEntryIndex, std::move(navigations));
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  base::android::ScopedJavaLocalRef<jobject> buffer_obj =
-      original_unpacked.Pack(env);
-  ASSERT_TRUE(buffer_obj);
-
-  base::span<const uint8_t> buffer_span =
-      base::android::JavaByteBufferToSpan(env, buffer_obj);
-  ASSERT_FALSE(buffer_span.empty());
-
-  base::TimeTicks start_time = base::TimeTicks::Now();
-  std::unique_ptr<WebContentsStateUnpacked> unpacked =
-      WebContentsState::Unpack(buffer_span, kSavedStateVersion);
-  base::TimeDelta unpack_duration = base::TimeTicks::Now() - start_time;
-  LOG(INFO) << "Unpack duration: " << unpack_duration.InMicroseconds() << "us";
-  ASSERT_TRUE(unpacked);
-
-  EXPECT_EQ(unpacked->is_off_the_record(), kIsOffTheRecord);
-  EXPECT_EQ(unpacked->current_entry_index(), kCurrentEntryIndex);
-  ASSERT_EQ(unpacked->navigations().size(), navigations_for_comparison.size());
-  for (size_t i = 0; i < unpacked->navigations().size(); ++i) {
-    ExpectNavigationsEqual(navigations_for_comparison[i],
-                           unpacked->navigations()[i]);
-  }
-
-  size_t unpacked_size = CalculateUnpackedSize(*unpacked);
-  LOG(INFO) << "Buffer size (bytes): " << buffer_span.size()
-            << ", Unpacked size (bytes): " << unpacked_size;
-}
diff --git a/chrome/browser/tab_ui/android/java/res/values/colors.xml b/chrome/browser/tab_ui/android/java/res/values/colors.xml
index bd8074e..f6d33870 100644
--- a/chrome/browser/tab_ui/android/java/res/values/colors.xml
+++ b/chrome/browser/tab_ui/android/java/res/values/colors.xml
@@ -18,6 +18,7 @@
   <color name="incognito_tab_tile_number_selected_color">@color/baseline_primary_20</color>
 
   <color name="incognito_tab_action_button_selected_color">@color/baseline_primary_20</color>
+  <color name="incognito_tab_action_button_color">@color/baseline_neutral_variant_80</color>
 
   <color name="incognito_tab_card_hover_color_overlay">@color/baseline_neutral_100_alpha_16</color>
 </resources>
\ No newline at end of file
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabCardThemeUtil.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabCardThemeUtil.java
index 9023641..464f27a 100644
--- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabCardThemeUtil.java
+++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabCardThemeUtil.java
@@ -18,7 +18,6 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.browser.theme.ThemeModuleUtils;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.tab_groups.TabGroupColorId;
@@ -119,8 +118,14 @@
             @ColorInt int baseColor = SemanticColorUtils.getColorOnPrimary(context);
             return MaterialColors.compositeARGBWithAlpha(baseColor, alpha);
         }
-        return SurfaceColorUpdateUtils.getCardViewMiniThumbnailPlaceholderColor(
-                context, isIncognito, colorId);
+        if (useNewGm3GtsTabGroupColors() && colorId != null) {
+            return TabGroupColorPickerUtils.getTabGroupCardMiniThumbnailPlaceholderColor(
+                    context, colorId, isIncognito);
+        }
+        if (isIncognito) {
+            return context.getColor(R.color.incognito_tab_thumbnail_placeholder_color);
+        }
+        return SemanticColorUtils.getColorSurfaceContainerLow(context);
     }
 
     /**
@@ -198,8 +203,12 @@
                     ? context.getColor(R.color.incognito_tab_tile_number_selected_color)
                     : MaterialColors.getColor(context, R.attr.colorOnPrimary, TAG);
         }
-        return SurfaceColorUpdateUtils.getCardViewGroupNumberTextColor(
-                context, isIncognito, colorId);
+        if (useNewGm3GtsTabGroupColors() && colorId != null) {
+            return TabGroupColorPickerUtils.getTabGroupCardTextColor(context, colorId, isIncognito);
+        }
+        return isIncognito
+                ? context.getColor(R.color.incognito_tab_tile_number_color)
+                : SemanticColorUtils.getDefaultTextColor(context);
     }
 
     /**
@@ -223,7 +232,16 @@
                     : ColorStateList.valueOf(
                             MaterialColors.getColor(context, R.attr.colorOnPrimary, TAG));
         }
-        return SurfaceColorUpdateUtils.getCardViewActionButtonColor(context, isIncognito, colorId);
+        if (useNewGm3GtsTabGroupColors() && colorId != null) {
+            return ColorStateList.valueOf(
+                    TabGroupColorPickerUtils.getTabGroupCardTextColor(
+                            context, colorId, isIncognito));
+        }
+        return isIncognito
+                ? AppCompatResources.getColorStateList(
+                        context, R.color.incognito_tab_action_button_color)
+                : ColorStateList.valueOf(
+                        MaterialColors.getColor(context, R.attr.colorOnSurfaceVariant, TAG));
     }
 
     /**
diff --git a/chrome/browser/tab_ui/android/junit/src/org/chromium/chrome/browser/tab_ui/TabCardThemeUtilUnitTest.java b/chrome/browser/tab_ui/android/junit/src/org/chromium/chrome/browser/tab_ui/TabCardThemeUtilUnitTest.java
index 0e5e2af..f758e06 100644
--- a/chrome/browser/tab_ui/android/junit/src/org/chromium/chrome/browser/tab_ui/TabCardThemeUtilUnitTest.java
+++ b/chrome/browser/tab_ui/android/junit/src/org/chromium/chrome/browser/tab_ui/TabCardThemeUtilUnitTest.java
@@ -22,7 +22,6 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.tab_groups.TabGroupColorId;
 import org.chromium.components.tab_groups.TabGroupColorPickerUtils;
@@ -161,8 +160,8 @@
         @TabGroupColorId int testColorId = TabGroupColorId.GREEN;
         @ColorInt
         int expectedColor =
-                SurfaceColorUpdateUtils.getCardViewMiniThumbnailPlaceholderColor(
-                        mContext, /* isIncognito= */ false, testColorId);
+                TabGroupColorPickerUtils.getTabGroupCardMiniThumbnailPlaceholderColor(
+                        mContext, testColorId, /* isIncognito= */ false);
         @ColorInt
         int actualColor =
                 TabCardThemeUtil.getMiniThumbnailPlaceholderColor(
@@ -250,8 +249,8 @@
         @TabGroupColorId int testColorId = TabGroupColorId.CYAN;
         @ColorInt
         int expectedColor =
-                SurfaceColorUpdateUtils.getCardViewGroupNumberTextColor(
-                        mContext, /* isIncognito= */ false, testColorId);
+                TabGroupColorPickerUtils.getTabGroupCardTextColor(
+                        mContext, testColorId, /* isIncognito= */ false);
         @ColorInt
         int actualColor =
                 TabCardThemeUtil.getTabGroupNumberTextColor(
@@ -292,8 +291,9 @@
         // This test verifies that the method correctly delegates to SurfaceColorUpdateUtils.
         @TabGroupColorId int testColorId = TabGroupColorId.ORANGE;
         ColorStateList expectedColor =
-                SurfaceColorUpdateUtils.getCardViewActionButtonColor(
-                        mContext, /* isIncognito= */ false, testColorId);
+                ColorStateList.valueOf(
+                        TabGroupColorPickerUtils.getTabGroupCardTextColor(
+                                mContext, testColorId, /* isIncognito= */ false));
         ColorStateList actualColor =
                 TabCardThemeUtil.getActionButtonTintList(
                         mContext, /* isIncognito= */ false, /* isSelected= */ false, testColorId);
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModel.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModel.java
index 46b35cfe..0378425 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModel.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModel.java
@@ -159,7 +159,10 @@
     public void moveTab(int id, int newIndex) {}
 
     @Override
-    public void pinTab(int tabId) {}
+    public void pinTab(
+            int tabId,
+            boolean showUngroupDialog,
+            @Nullable TabModelActionListener tabModelActionListener) {}
 
     @Override
     public void unpinTab(int tabId) {}
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImpl.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImpl.java
index 54444ec..1c7eb67 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImpl.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImpl.java
@@ -271,8 +271,11 @@
     }
 
     @Override
-    public void pinTab(int tabId) {
-        mDelegateModel.pinTab(tabId);
+    public void pinTab(
+            int tabId,
+            boolean showUngroupDialog,
+            @Nullable TabModelActionListener tabModelActionListener) {
+        mDelegateModel.pinTab(tabId, showUngroupDialog, tabModelActionListener);
     }
 
     @Override
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java
index b2eefe97..183103e25 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java
@@ -1448,18 +1448,6 @@
     }
 
     @Override
-    public void willChangePinState(Tab tab) {
-        assert !(tab.getIsPinned() && isTabInTabGroup(tab))
-                : "A pinned tab should not be in a group";
-
-        // If tab is about to get pinned state and it is in a tab group
-        if (!tab.getIsPinned() && isTabInTabGroup(tab)) {
-            mTabUngrouper.ungroupTabs(
-                    Collections.singletonList(tab), /* trailing= */ false, /* allowDialog= */ true);
-        }
-    }
-
-    @Override
     public Set<Token> getAllTabGroupIds() {
         Set<Token> uniqueTabGroupIds = new ArraySet<>();
         TabList tabList = getTabModel();
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImplUnitTest.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImplUnitTest.java
index 4371ec8..2aeff8dd 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImplUnitTest.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImplUnitTest.java
@@ -2900,25 +2900,6 @@
         assertFalse(mTabGroupModelFilter.willMergingCreateNewGroup(tabsToMerge));
     }
 
-    @Test
-    public void testWillChangePinState_TabWillPin_TabInExistingGroup() {
-        doReturn(false).when(mTab3).getIsPinned();
-
-        mTabGroupModelFilter.willChangePinState(mTab3);
-
-        verify(mTabUngrouper)
-                .ungroupTabs(List.of(mTab3), /* trailing= */ false, /* allowDialog= */ true);
-    }
-
-    @Test
-    public void testWillChangePinState_TabWillUnPin_TabNotIngroup_NoOp() {
-        doReturn(true).when(mTab1).getIsPinned();
-
-        mTabGroupModelFilter.willChangePinState(mTab1);
-
-        verify(mTabUngrouper, never()).ungroupTabs(any(), anyBoolean(), anyBoolean());
-    }
-
     private void verifyGroupCreationDialogShouldShow(VerificationMode mode) {
         mTabGroupModelFilter.mergeTabsToGroup(mTab1.getId(), mTab4.getId());
 
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModel.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModel.java
index 389b9f2..85a5cf2 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModel.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModel.java
@@ -137,8 +137,26 @@
      * Pins a tab to the model.
      *
      * @param tabId The id of the tab to pin.
+     * @param showUngroupDialog Whether to possibly show a dialog to the user when pinning the last
+     *     tab in a group.
      */
-    void pinTab(int tabId);
+    default void pinTab(int tabId, boolean showUngroupDialog) {
+        pinTab(tabId, showUngroupDialog, /* tabModelActionListener= */ null);
+    }
+
+    /**
+     * Pins a tab to the model.
+     *
+     * @param tabId The id of the tab to pin.
+     * @param showUngroupDialog Whether to possibly show a dialog to the user when pinning the last
+     *     tab in a group.
+     * @param tabModelActionListener A listener that is notified in response to the user actions
+     *     taken in the ungroup dialog (if shown).
+     */
+    void pinTab(
+            int tabId,
+            boolean showUngroupDialog,
+            @Nullable TabModelActionListener tabModelActionListener);
 
     /**
      * Unpins a tab from the model.
diff --git a/chrome/browser/task_manager/providers/worker_task_provider_browsertest.cc b/chrome/browser/task_manager/providers/worker_task_provider_browsertest.cc
index 1885fc9..b578c5f 100644
--- a/chrome/browser/task_manager/providers/worker_task_provider_browsertest.cc
+++ b/chrome/browser/task_manager/providers/worker_task_provider_browsertest.cc
@@ -20,6 +20,9 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -51,8 +54,9 @@
 }
 
 // Get the process id of the active WebContents for the passed |browser|.
-int GetChildProcessID(Browser* browser) {
-  return browser->tab_strip_model()
+int GetChildProcessID(BrowserWindowInterface* browser) {
+  return browser->GetFeatures()
+      .tab_strip_model()
       ->GetActiveWebContents()
       ->GetPrimaryMainFrame()
       ->GetProcess()
@@ -83,7 +87,7 @@
     task_provider_.reset();
   }
 
-  Browser* CreateNewProfileAndSwitch() {
+  BrowserWindowInterface* CreateNewProfileAndSwitch() {
     ProfileManager* profile_manager = g_browser_process->profile_manager();
     base::FilePath new_path =
         profile_manager->GenerateNextProfileDirectoryPath();
@@ -92,19 +96,20 @@
 
     profiles::SwitchToProfile(new_path, /* always_create = */ false,
                               base::DoNothing());
-    BrowserList* browser_list = BrowserList::GetInstance();
-    return *browser_list->begin_browsers_ordered_by_activation();
+    return GetLastActiveBrowserWindowInterfaceWithAnyProfile();
   }
 
-  content::ServiceWorkerContext* GetServiceWorkerContext(Browser* browser) {
-    return browser->profile()
+  content::ServiceWorkerContext* GetServiceWorkerContext(
+      BrowserWindowInterface* browser) {
+    return browser->GetProfile()
         ->GetDefaultStoragePartition()
         ->GetServiceWorkerContext();
   }
 
   void WaitUntilTaskCount(uint64_t count) {
-    if (tasks_.size() == count)
+    if (tasks_.size() == count) {
       return;
+    }
 
     expected_task_count_ = count;
     base::RunLoop loop;
@@ -117,16 +122,18 @@
     DCHECK(task);
     tasks_.push_back(task);
 
-    if (expected_task_count_ == tasks_.size())
+    if (expected_task_count_ == tasks_.size()) {
       StopWaiting();
+    }
   }
 
   void TaskRemoved(Task* task) override {
     DCHECK(task);
     std::erase(tasks_, task);
 
-    if (expected_task_count_ == tasks_.size())
+    if (expected_task_count_ == tasks_.size()) {
       StopWaiting();
+    }
   }
 
   const std::vector<raw_ptr<Task, VectorExperimental>>& tasks() const {
@@ -143,8 +150,9 @@
   }
 
   void StopWaiting() {
-    if (quit_closure_for_waiting_)
+    if (quit_closure_for_waiting_) {
       std::move(quit_closure_for_waiting_).Run();
+    }
   }
 
  private:
@@ -243,7 +251,7 @@
   const GURL kCreateServiceWorkerURL = embedded_test_server()->GetURL(
       "/service_worker/create_service_worker.html");
 
-  Browser* browser_1 = CreateNewProfileAndSwitch();
+  BrowserWindowInterface* browser_1 = CreateNewProfileAndSwitch();
   content::RenderFrameHost* render_frame_host_1 =
       ui_test_utils::NavigateToURL(browser_1, kCreateServiceWorkerURL);
   ASSERT_EQ(render_frame_host_1->GetLastCommittedURL(),
@@ -252,7 +260,7 @@
                            "register('respond_with_fetch_worker.js');"));
   WaitUntilTaskCount(1);
 
-  Browser* browser_2 = CreateNewProfileAndSwitch();
+  BrowserWindowInterface* browser_2 = CreateNewProfileAndSwitch();
   content::RenderFrameHost* render_frame_host_2 =
       ui_test_utils::NavigateToURL(browser_2, kCreateServiceWorkerURL);
   ASSERT_EQ(render_frame_host_2->GetLastCommittedURL(),
diff --git a/chrome/browser/task_manager/task_manager_interface.cc b/chrome/browser/task_manager/task_manager_interface.cc
index a41fe37e..679f980 100644
--- a/chrome/browser/task_manager/task_manager_interface.cc
+++ b/chrome/browser/task_manager/task_manager_interface.cc
@@ -68,6 +68,10 @@
     StartUpdating();
   }
 
+  // Observer was removed as part of StartUpdating.
+  if (!observers_.HasObserver(observer)) {
+    return;
+  }
   if (observer->desired_refresh_time() > current_refresh_time)
     return;
 
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/BUILD.gn b/chrome/browser/touch_to_fill/autofill/android/internal/BUILD.gn
index db55d6e..d0c3cf3 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/BUILD.gn
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/BUILD.gn
@@ -49,8 +49,10 @@
     "java/res/layout/touch_to_fill_all_loyalty_cards_item.xml",
     "java/res/layout/touch_to_fill_bnpl_issuer_selection_screen.xml",
     "java/res/layout/touch_to_fill_bnpl_issuer_selection_sheet_item.xml",
+    "java/res/layout/touch_to_fill_bnpl_issuer_tos_screen.xml",
     "java/res/layout/touch_to_fill_bnpl_selection_and_progress_screen_header_item.xml",
     "java/res/layout/touch_to_fill_bnpl_sheet_item.xml",
+    "java/res/layout/touch_to_fill_bnpl_tos_sheet_item.xml",
     "java/res/layout/touch_to_fill_credit_card_sheet_item.xml",
     "java/res/layout/touch_to_fill_error_description_sheet_item.xml",
     "java/res/layout/touch_to_fill_error_screen.xml",
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_bnpl_issuer_tos_screen.xml b/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_bnpl_issuer_tos_screen.xml
new file mode 100644
index 0000000..0d0a748
--- /dev/null
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_bnpl_issuer_tos_screen.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2025 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<androidx.recyclerview.widget.RecyclerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/touch_to_fill_bnpl_tos_screen_list"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginEnd="@dimen/ttf_sheet_margin"
+    android:layout_marginStart="@dimen/ttf_sheet_margin"
+    android:clipToPadding="false"
+    android:divider="@null"/>
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_bnpl_tos_sheet_item.xml b/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_bnpl_tos_sheet_item.xml
new file mode 100644
index 0000000..79f363a6
--- /dev/null
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_bnpl_tos_sheet_item.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2025 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<!-- TODO(crbug.com/438784697): Set sheet item background to transparent. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/touch_to_fill_bnpl_tos_sheet_item"
+    android:descendantFocusability="blocksDescendants"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginBottom="@dimen/ttf_for_sheet_item_margin_bottom"
+    android:layout_marginHorizontal="@dimen/ttf_for_sheet_item_margin_horizontal"
+    android:background="@android:color/transparent"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:padding="@dimen/ttf_for_sheet_item_padding">
+
+  <ImageView
+      android:id="@+id/bnpl_tos_icon"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_marginEnd="@dimen/ttf_for_sheet_item_icon_margin_end"
+      android:importantForAccessibility="no"
+      android:layout_gravity="center"/>
+  <org.chromium.ui.widget.TextViewWithClickableSpans
+      android:id="@+id/bnpl_tos_text"
+      android:layout_width="0dp"
+      android:layout_weight="1"
+      android:gravity="top|start"
+      android:layout_height="wrap_content"
+      android:layout_marginEnd="@dimen/bnpl_tos_sheet_item_margin_end"
+      android:textAppearance="@style/TextAppearance.TextLarge.Primary"/>
+
+</LinearLayout>
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_payment_method_sheet.xml b/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_payment_method_sheet.xml
index bac55110..1b62533 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_payment_method_sheet.xml
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_payment_method_sheet.xml
@@ -52,6 +52,10 @@
         <include
             android:id="@+id/touch_to_fill_error_screen"
             layout="@layout/touch_to_fill_error_screen" />
+
+        <include
+            android:id="@+id/touch_to_fill_bnpl_issuer_tos_screen"
+            layout="@layout/touch_to_fill_bnpl_issuer_tos_screen" />
     </ViewFlipper>
 
 
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/res/values/dimens.xml b/chrome/browser/touch_to_fill/autofill/android/internal/java/res/values/dimens.xml
index f247836..55326cb 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/res/values/dimens.xml
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/res/values/dimens.xml
@@ -25,4 +25,5 @@
     <dimen name="bnpl_issuer_linked_status_pill_padding_horizontal">8dp</dimen>
     <dimen name="bnpl_issuer_linked_status_pill_padding_vertical">2dp</dimen>
     <dimen name="bnpl_header_margin_bottom">10dp</dimen>
+    <dimen name="bnpl_tos_sheet_item_margin_end">16dp</dimen>
 </resources>
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodCoordinator.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodCoordinator.java
index 92679dc..b5780af 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodCoordinator.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodCoordinator.java
@@ -14,6 +14,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.BNPL;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.BNPL_ISSUER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.BNPL_SELECTION_PROGRESS_HEADER;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.BNPL_TOS_TEXT;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.CREDIT_CARD;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.ERROR_DESCRIPTION;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.FILL_BUTTON;
@@ -208,6 +209,10 @@
                 ERROR_DESCRIPTION,
                 TouchToFillPaymentMethodViewBinder::createErrorDescriptionView,
                 TouchToFillPaymentMethodViewBinder::bindErrorDescriptionView);
+        adapter.registerType(
+                BNPL_TOS_TEXT,
+                TouchToFillPaymentMethodViewBinder::createBnplIssuerTosItemView,
+                TouchToFillPaymentMethodViewBinder::bindBnplIssuerTosItemView);
         view.setSheetItemListAdapter(adapter);
     }
 
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodMediator.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodMediator.java
index 4fce666..17a5aa2 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodMediator.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodMediator.java
@@ -47,6 +47,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.BNPL;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.BNPL_ISSUER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.BNPL_SELECTION_PROGRESS_HEADER;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.BNPL_TOS_TEXT;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.CREDIT_CARD;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.ERROR_DESCRIPTION;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.FILL_BUTTON;
@@ -70,6 +71,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.ALL_LOYALTY_CARDS_SCREEN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.BNPL_ISSUER_SELECTION_SCREEN;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.BNPL_ISSUER_TOS_SCREEN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.ERROR_SCREEN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.HOME_SCREEN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.PROGRESS_SCREEN;
@@ -95,6 +97,7 @@
 import org.chromium.chrome.browser.touch_to_fill.common.TouchToFillResourceProvider;
 import org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodComponent.Delegate;
 import org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.AllLoyaltyCardsItemProperties;
+import org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplIssuerTosTextItemProperties;
 import org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplSelectionProgressHeaderProperties;
 import org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ButtonProperties;
 import org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ErrorDescriptionProperties;
@@ -108,6 +111,7 @@
 import org.chromium.components.autofill.LoyaltyCard;
 import org.chromium.components.autofill.PaymentsPayload;
 import org.chromium.components.autofill.SuggestionType;
+import org.chromium.components.autofill.payments.BnplIssuerTosDetail;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason;
 import org.chromium.components.payments.ui.InputProtector;
@@ -602,6 +606,35 @@
         mModel.set(VISIBLE, true);
     }
 
+    /**
+     * Displays the BNPL issuer ToS screen.
+     *
+     * <p>This method shows a bottom sheet showing the BNPL issuer ToS info.
+     *
+     * @param BnplIssuerTosDetail A struct with text and icon to be shown.
+     */
+    public void showBnplIssuerTos(BnplIssuerTosDetail bnplIssuerTosDetail) {
+        ModelList sheetItems = new ModelList();
+        sheetItems.add(
+                new ListItem(
+                        BNPL_TOS_TEXT,
+                        createBnplIssuerTosTextItemModel(
+                                R.drawable.checklist, bnplIssuerTosDetail.getReviewText())));
+        sheetItems.add(
+                new ListItem(
+                        BNPL_TOS_TEXT,
+                        createBnplIssuerTosTextItemModel(
+                                R.drawable.receipt_long, bnplIssuerTosDetail.getApproveText())));
+        sheetItems.add(
+                new ListItem(
+                        BNPL_TOS_TEXT,
+                        createBnplIssuerTosTextItemModel(
+                                R.drawable.add_link, bnplIssuerTosDetail.getLinkText())));
+
+        mModel.set(CURRENT_SCREEN, BNPL_ISSUER_TOS_SCREEN);
+        mModel.set(SHEET_ITEMS, sheetItems);
+    }
+
     void hideSheet() {
         onDismissed(BottomSheetController.StateChangeReason.NONE);
     }
@@ -867,6 +900,13 @@
                 .build();
     }
 
+    private PropertyModel createBnplIssuerTosTextItemModel(int iconDrawableId, CharSequence text) {
+        return new PropertyModel.Builder(BnplIssuerTosTextItemProperties.ALL_KEYS)
+                .with(BnplIssuerTosTextItemProperties.BNPL_TOS_ICON_ID, iconDrawableId)
+                .with(BnplIssuerTosTextItemProperties.DESCRIPTION_TEXT, text)
+                .build();
+    }
+
     private PropertyModel createFillButtonModel(@StringRes int titleId, Runnable onClickAction) {
         return new PropertyModel.Builder(ButtonProperties.ALL_KEYS)
                 .with(TEXT_ID, titleId)
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodProperties.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodProperties.java
index 348954d..137b1f7 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodProperties.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodProperties.java
@@ -72,6 +72,9 @@
 
         // The screen displaying the error message and "OK" button.
         int ERROR_SCREEN = 4;
+
+        // The screen displaying the legal messages for linking a new BNPL issuer.
+        int BNPL_ISSUER_TOS_SCREEN = 5;
     }
 
     @interface ItemType {
@@ -117,6 +120,9 @@
 
         // A section containing the error description.
         int ERROR_DESCRIPTION = 13;
+
+        // A section contains texts shown on BNPL ToS screen.
+        int BNPL_TOS_TEXT = 14;
     }
 
     /** Metadata associated with a card's image. */
@@ -223,6 +229,18 @@
         private AllLoyaltyCardsItemProperties() {}
     }
 
+    /** Properties for the BNPL ToS screen item in the TouchToFill sheet for payments. */
+    static class BnplIssuerTosTextItemProperties {
+        static final PropertyModel.ReadableIntPropertyKey BNPL_TOS_ICON_ID =
+                new PropertyModel.ReadableIntPropertyKey("bnpl_tos_icon_id");
+        static final PropertyModel.ReadableObjectPropertyKey<CharSequence> DESCRIPTION_TEXT =
+                new PropertyModel.ReadableObjectPropertyKey<>("description_text");
+
+        static final PropertyKey[] ALL_KEYS = {BNPL_TOS_ICON_ID, DESCRIPTION_TEXT};
+
+        private BnplIssuerTosTextItemProperties() {}
+    }
+
     /**
      * Properties defined here reflect the visible state of the terms message in the TouchToFill
      * sheet for payments.
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodView.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodView.java
index 28db183..4b2fa99 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodView.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodView.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.ALL_LOYALTY_CARDS_SCREEN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.BNPL_ISSUER_SELECTION_SCREEN;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.BNPL_ISSUER_TOS_SCREEN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.ERROR_SCREEN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.HOME_SCREEN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.PROGRESS_SCREEN;
@@ -68,6 +69,7 @@
                 case ItemType.ALL_LOYALTY_CARDS:
                 case ItemType.BNPL:
                 case ItemType.BNPL_ISSUER:
+                case ItemType.BNPL_TOS_TEXT:
                     return false;
             }
             assert false : "Undefined whether to skip setting background for item of type: " + type;
@@ -208,6 +210,8 @@
                 return 3;
             case ERROR_SCREEN:
                 return 4;
+            case BNPL_ISSUER_TOS_SCREEN:
+                return 5;
         }
         assert false : "Undefined ScreenId: " + screenId;
         return 0;
@@ -225,6 +229,8 @@
                 return R.id.touch_to_fill_bnpl_issuer_selection_screen;
             case ERROR_SCREEN:
                 return R.id.touch_to_fill_error_screen;
+            case BNPL_ISSUER_TOS_SCREEN:
+                return R.id.touch_to_fill_bnpl_issuer_tos_screen;
         }
         assert false : "Undefined ScreenId: " + screenId;
         return 0;
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewBinder.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewBinder.java
index d39c863..5e1ba318 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewBinder.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewBinder.java
@@ -9,6 +9,8 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplIssuerProperties.ISSUER_LINKED;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplIssuerProperties.ISSUER_NAME;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplIssuerProperties.ON_ISSUER_CLICK_ACTION;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplIssuerTosTextItemProperties.BNPL_TOS_ICON_ID;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplIssuerTosTextItemProperties.DESCRIPTION_TEXT;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplSelectionProgressHeaderProperties.BNPL_BACK_BUTTON_ENABLED;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplSelectionProgressHeaderProperties.BNPL_ON_BACK_BUTTON_CLICKED;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplSuggestionProperties.BNPL_ICON_ID;
@@ -57,6 +59,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.VISIBLE;
 
 import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -201,6 +204,14 @@
         return loyaltyCardItem;
     }
 
+    static View createBnplIssuerTosItemView(ViewGroup parent) {
+        View bnplIssuerTosItem =
+                LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.touch_to_fill_bnpl_tos_sheet_item, parent, false);
+        AutofillUiUtils.setFilterTouchForSecurity(bnplIssuerTosItem);
+        return bnplIssuerTosItem;
+    }
+
     /** Binds the item view to the model properties. */
     static void bindCardItemView(PropertyModel model, View view, PropertyKey propertyKey) {
         TextView mainText = view.findViewById(R.id.main_text);
@@ -619,6 +630,27 @@
     }
 
     /**
+     * Called whenever a property in the given model changes. It updates the given view accordingly.
+     *
+     * @param model The observed {@link PropertyModel}. Its data need to be reflected in the view.
+     * @param view The {@link View} of the header to update.
+     * @param propertyKey The {@link PropertyKey} which changed.
+     */
+    static void bindBnplIssuerTosItemView(PropertyModel model, View view, PropertyKey propertyKey) {
+        if (propertyKey == BNPL_TOS_ICON_ID) {
+            ImageView iconView = view.findViewById(R.id.bnpl_tos_icon);
+            iconView.setImageDrawable(
+                    AppCompatResources.getDrawable(view.getContext(), model.get(BNPL_TOS_ICON_ID)));
+        } else if (propertyKey == DESCRIPTION_TEXT) {
+            TextView textView = view.findViewById(R.id.bnpl_tos_text);
+            textView.setText(model.get(DESCRIPTION_TEXT), TextView.BufferType.SPANNABLE);
+            textView.setMovementMethod(LinkMovementMethod.getInstance());
+        } else {
+            assert false : "Unhandled update to property:" + propertyKey;
+        }
+    }
+
+    /**
      * Factory used to create a new footer inside the ListView inside the
      * TouchToFillPaymentMethodView.
      *
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewTest.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewTest.java
index 7484d15d..36e4a1f 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewTest.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewTest.java
@@ -36,6 +36,8 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplIssuerProperties.ISSUER_NAME;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplIssuerProperties.NON_TRANSFORMING_BNPL_ISSUER_SUGGESTION_KEYS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplIssuerProperties.ON_ISSUER_CLICK_ACTION;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplIssuerTosTextItemProperties.BNPL_TOS_ICON_ID;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplIssuerTosTextItemProperties.DESCRIPTION_TEXT;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplSelectionProgressHeaderProperties.BNPL_BACK_BUTTON_ENABLED;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplSelectionProgressHeaderProperties.BNPL_ON_BACK_BUTTON_CLICKED;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.BnplSuggestionProperties.BNPL_ICON_ID;
@@ -69,6 +71,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.BNPL;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.BNPL_ISSUER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.BNPL_SELECTION_PROGRESS_HEADER;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.BNPL_TOS_TEXT;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.CREDIT_CARD;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.ERROR_DESCRIPTION;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.FILL_BUTTON;
@@ -89,6 +92,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.ALL_LOYALTY_CARDS_SCREEN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.BNPL_ISSUER_SELECTION_SCREEN;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.BNPL_ISSUER_TOS_SCREEN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.ERROR_SCREEN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.HOME_SCREEN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ScreenId.PROGRESS_SCREEN;
@@ -98,6 +102,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodViewBinder.COMPLETE_OPACITY_ALPHA;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodViewBinder.GRAYED_OUT_OPACITY_ALPHA;
 
+import android.text.SpannableString;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -385,6 +390,7 @@
                     /* displayName= */ "Affirm",
                     /* iconId= */ R.drawable.affirm_unlinked,
                     /* isLinked= */ false);
+    private static final String BNPL_ISSUER_TOS_ITEM_TEXT = "Affirm ToS text";
 
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
@@ -1273,6 +1279,45 @@
 
     @Test
     @MediumTest
+    public void testBnplTosScreenShown() {
+        runOnUiThreadBlocking(
+                () -> {
+                    ModelList bnplTosScreenItems = new ModelList();
+                    bnplTosScreenItems.add(
+                            new ListItem(
+                                    BNPL_TOS_TEXT, createBnplIssuerTosTextItemModelWithLinkText()));
+                    bnplTosScreenItems.add(
+                            new ListItem(
+                                    BNPL_TOS_TEXT, createBnplIssuerTosTextItemModelWithFlatText()));
+
+                    mTouchToFillPaymentMethodModel.set(CURRENT_SCREEN, BNPL_ISSUER_TOS_SCREEN);
+                    mTouchToFillPaymentMethodModel.set(SHEET_ITEMS, bnplTosScreenItems);
+                    mTouchToFillPaymentMethodModel.set(VISIBLE, true);
+                });
+        BottomSheetTestSupport.waitForOpen(mBottomSheetController);
+
+        RecyclerView bnplTosScreen =
+                mTouchToFillPaymentMethodView
+                        .getContentView()
+                        .findViewById(R.id.touch_to_fill_bnpl_issuer_tos_screen);
+        assertNotNull(bnplTosScreen);
+        assertThat(bnplTosScreen.getAdapter().getItemCount(), is(2));
+
+        TextView textView = bnplTosScreen.getChildAt(0).findViewById(R.id.bnpl_tos_text);
+        assertTrue(textView.isShown());
+        assertThat(textView.getText().toString(), is(BNPL_ISSUER_TOS_ITEM_TEXT));
+        textView = bnplTosScreen.getChildAt(1).findViewById(R.id.bnpl_tos_text);
+        assertTrue(textView.isShown());
+        assertThat(textView.getText().toString(), is(BNPL_ISSUER_TOS_ITEM_TEXT));
+
+        ImageView imageView = bnplTosScreen.getChildAt(0).findViewById(R.id.bnpl_tos_icon);
+        assertTrue(imageView.isShown());
+        imageView = bnplTosScreen.getChildAt(1).findViewById(R.id.bnpl_tos_icon);
+        assertTrue(imageView.isShown());
+    }
+
+    @Test
+    @MediumTest
     public void testBnplSuggestionView() {
         runOnUiThreadBlocking(
                 () -> {
@@ -1767,6 +1812,22 @@
                 .build();
     }
 
+    private static PropertyModel createBnplIssuerTosTextItemModelWithLinkText() {
+        return new PropertyModel.Builder(
+                        TouchToFillPaymentMethodProperties.BnplIssuerTosTextItemProperties.ALL_KEYS)
+                .with(BNPL_TOS_ICON_ID, R.drawable.add_link)
+                .with(DESCRIPTION_TEXT, new SpannableString(BNPL_ISSUER_TOS_ITEM_TEXT))
+                .build();
+    }
+
+    private static PropertyModel createBnplIssuerTosTextItemModelWithFlatText() {
+        return new PropertyModel.Builder(
+                        TouchToFillPaymentMethodProperties.BnplIssuerTosTextItemProperties.ALL_KEYS)
+                .with(BNPL_TOS_ICON_ID, R.drawable.checklist)
+                .with(DESCRIPTION_TEXT, BNPL_ISSUER_TOS_ITEM_TEXT)
+                .build();
+    }
+
     private static PropertyModel createBnplSelectionProgressHeaderModel(
             boolean backButtonEnabled, Runnable actionCallback) {
         return new PropertyModel.Builder(BnplSelectionProgressHeaderProperties.ALL_KEYS)
diff --git a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/BUILD.gn b/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/BUILD.gn
deleted file mode 100644
index 357ece39..0000000
--- a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/BUILD.gn
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright 2025 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/android/rules.gni")
-import("//third_party/jni_zero/jni_zero.gni")
-
-source_set("public") {
-  deps = [
-    ":android",
-    "//base",
-    "//components/prefs",
-    "//content/public/browser",
-  ]
-
-  sources = [
-    "touch_to_fill_one_time_tokens_controller.cc",
-    "touch_to_fill_one_time_tokens_controller.h",
-  ]
-}
-
-source_set("android") {
-  sources = [
-    "touch_to_fill_one_time_tokens_bridge.h",
-    "touch_to_fill_one_time_tokens_bridge_impl.cc",
-    "touch_to_fill_one_time_tokens_bridge_impl.h",
-    "touch_to_fill_one_time_tokens_delegate.h",
-  ]
-  deps = [
-    "//chrome/browser/touch_to_fill/autofill/one_time_tokens/android/internal:jni",
-    "//content/public/browser",
-    "//ui/android",
-  ]
-}
-
-source_set("test_support") {
-  testonly = true
-
-  deps = [
-    ":android",
-    "//base",
-    "//content/public/browser",
-    "//testing/gmock",
-    "//ui/gfx:native_widget_types",
-  ]
-
-  sources = [
-    "mock_touch_to_fill_one_time_tokens_bridge.cc",
-    "mock_touch_to_fill_one_time_tokens_bridge.h",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [ "touch_to_fill_one_time_tokens_controller_unittest.cc" ]
-  deps = [
-    ":public",
-    ":test_support",
-    "//base",
-    "//chrome/test:test_support",
-    "//content/test:test_support",
-  ]
-}
diff --git a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/internal/BUILD.gn b/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/internal/BUILD.gn
deleted file mode 100644
index 95f11f9..0000000
--- a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/internal/BUILD.gn
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2025 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/android/rules.gni")
-import("//third_party/jni_zero/jni_zero.gni")
-
-generate_jni("jni") {
-  sources = [ "java/src/org/chromium/chrome/browser/touch_to_fill/one_time_tokens/TouchToFillOneTimeTokensBridge.java" ]
-}
-
-android_library("java") {
-  deps = [
-    "//content/public/android:content_full_java",
-    "//third_party/jni_zero:gendeps_java",
-    "//third_party/jni_zero:jni_zero_java",
-    "//ui/android:ui_java",
-  ]
-  srcjar_deps = [ ":jni" ]
-  sources = [ "java/src/org/chromium/chrome/browser/touch_to_fill/one_time_tokens/TouchToFillOneTimeTokensBridge.java" ]
-
-  resources_package =
-      "org.chromium.chrome.browser.touch_to_fill.one_time_tokens"
-}
-
-robolectric_library("junit") {
-  testonly = true
-  resources_package =
-      "org.chromium.chrome.browser.touch_to_fill.password_generation"
-
-  sources = [ "java/src/org/chromium/chrome/browser/touch_to_fill/one_time_tokens/TouchToFillOneTimeTokensBridgeTest.java" ]
-  deps = [
-    ":java",
-    "//base:base_java_test_support",
-    "//base:base_junit_test_support",
-    "//content/public/android:content_full_java",
-    "//third_party/junit",
-    "//third_party/mockito:mockito_java",
-    "//ui/android:ui_java",
-    "//ui/android:ui_java_test_support",
-  ]
-}
diff --git a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/one_time_tokens/TouchToFillOneTimeTokensBridge.java b/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/one_time_tokens/TouchToFillOneTimeTokensBridge.java
deleted file mode 100644
index 1e7bfef6..0000000
--- a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/one_time_tokens/TouchToFillOneTimeTokensBridge.java
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.touch_to_fill.one_time_tokens;
-
-import android.content.Context;
-
-import org.jni_zero.CalledByNative;
-import org.jni_zero.NativeMethods;
-
-import org.chromium.build.annotations.NullMarked;
-import org.chromium.ui.base.WindowAndroid;
-
-/**
- * The JNI bridge for the one-time tokens Touch-To-Fill component. This class is instantiated by the
- * C++ side and acts as a client to the Java component.
- */
-@NullMarked
-class TouchToFillOneTimeTokensBridge {
-    private long mNativeBridge;
-    private final WindowAndroid mWindowAndroid;
-
-    TouchToFillOneTimeTokensBridge(WindowAndroid windowAndroid, long nativeBridge) {
-        mNativeBridge = nativeBridge;
-        mWindowAndroid = windowAndroid;
-    }
-
-    @CalledByNative
-    private static TouchToFillOneTimeTokensBridge create(
-            WindowAndroid windowAndroid, long nativeBridge) {
-        return new TouchToFillOneTimeTokensBridge(windowAndroid, nativeBridge);
-    }
-
-    @CalledByNative
-    public boolean show(String token) {
-        Context context = mWindowAndroid.getContext().get();
-        if (context == null) return false;
-        // TODO(crbug.com/444409226): Create and show the bottom sheet UI.
-        return true;
-    }
-
-    public void onDismissed(boolean tokenAccepted) {
-        if (mNativeBridge == 0) return;
-
-        TouchToFillOneTimeTokensBridgeJni.get().onDismissed(mNativeBridge, tokenAccepted);
-        mNativeBridge = 0;
-    }
-
-    public void onTokenAccepted(String token) {
-        if (mNativeBridge == 0) return;
-
-        TouchToFillOneTimeTokensBridgeJni.get().onTokenAccepted(mNativeBridge, token);
-    }
-
-    public void onTokenRejected() {
-        if (mNativeBridge == 0) return;
-
-        TouchToFillOneTimeTokensBridgeJni.get().onTokenRejected(mNativeBridge);
-    }
-
-    @CalledByNative
-    public void hide() {
-        mNativeBridge = 0;
-    }
-
-    @NativeMethods
-    interface Natives {
-        void onDismissed(long nativeTouchToFillOneTimeTokensBridgeImpl, boolean tokenAccepted);
-
-        void onTokenAccepted(long nativeTouchToFillOneTimeTokensBridgeImpl, String token);
-
-        void onTokenRejected(long nativeTouchToFillOneTimeTokensBridgeImpl);
-    }
-}
diff --git a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/one_time_tokens/TouchToFillOneTimeTokensBridgeTest.java b/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/one_time_tokens/TouchToFillOneTimeTokensBridgeTest.java
deleted file mode 100644
index 2ab16a9f..0000000
--- a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/one_time_tokens/TouchToFillOneTimeTokensBridgeTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.touch_to_fill.one_time_tokens;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.ui.base.WindowAndroid;
-
-import java.lang.ref.WeakReference;
-
-@RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class TouchToFillOneTimeTokensBridgeTest {
-    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
-    private TouchToFillOneTimeTokensBridge mBridge;
-
-    @Mock private WindowAndroid mWindowAndroid;
-    @Mock private Context mContext;
-
-    @Before
-    public void setUp() {
-        when(mWindowAndroid.getContext()).thenReturn(new WeakReference<>(mContext));
-        mBridge = new TouchToFillOneTimeTokensBridge(mWindowAndroid, /* nativeBridge= */ 0L);
-    }
-
-    @Test
-    public void testBridgeIsCreated() {
-        assertNotNull(mBridge);
-    }
-
-    @Test
-    public void testShowReturnsTrue() {
-        assertTrue(mBridge.show("test_token"));
-    }
-}
diff --git a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/mock_touch_to_fill_one_time_tokens_bridge.cc b/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/mock_touch_to_fill_one_time_tokens_bridge.cc
deleted file mode 100644
index 924d297..0000000
--- a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/mock_touch_to_fill_one_time_tokens_bridge.cc
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/touch_to_fill/autofill/one_time_tokens/android/mock_touch_to_fill_one_time_tokens_bridge.h"
-
-MockTouchToFillOneTimeTokensBridge::MockTouchToFillOneTimeTokensBridge() =
-    default;
-MockTouchToFillOneTimeTokensBridge::~MockTouchToFillOneTimeTokensBridge() =
-    default;
diff --git a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/mock_touch_to_fill_one_time_tokens_bridge.h b/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/mock_touch_to_fill_one_time_tokens_bridge.h
deleted file mode 100644
index 716d587..0000000
--- a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/mock_touch_to_fill_one_time_tokens_bridge.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_MOCK_TOUCH_TO_FILL_ONE_TIME_TOKENS_BRIDGE_H_
-#define CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_MOCK_TOUCH_TO_FILL_ONE_TIME_TOKENS_BRIDGE_H_
-
-#include "chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_bridge.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-class MockTouchToFillOneTimeTokensBridge
-    : public TouchToFillOneTimeTokensBridge {
- public:
-  MockTouchToFillOneTimeTokensBridge();
-  ~MockTouchToFillOneTimeTokensBridge() override;
-
-  MOCK_METHOD(bool,
-              Show,
-              (content::WebContents*,
-               TouchToFillOneTimeTokensDelegate*,
-               const std::u16string&),
-              (override));
-  MOCK_METHOD(void, Hide, (), (override));
-};
-
-#endif  // CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_MOCK_TOUCH_TO_FILL_ONE_TIME_TOKENS_BRIDGE_H_
diff --git a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_bridge.h b/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_bridge.h
deleted file mode 100644
index d6cfae6..0000000
--- a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_bridge.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_TOUCH_TO_FILL_ONE_TIME_TOKENS_BRIDGE_H_
-#define CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_TOUCH_TO_FILL_ONE_TIME_TOKENS_BRIDGE_H_
-
-#include <string>
-
-namespace content {
-class WebContents;
-}
-
-class TouchToFillOneTimeTokensDelegate;
-
-// The C++ side of the JNI bridge for the one-time tokens Touch-To-Fill.
-class TouchToFillOneTimeTokensBridge {
- public:
-  virtual ~TouchToFillOneTimeTokensBridge() = default;
-
-  virtual bool Show(content::WebContents* web_contents,
-                    TouchToFillOneTimeTokensDelegate* delegate,
-                    const std::u16string& token) = 0;
-  virtual void Hide() = 0;
-};
-
-#endif  // CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_TOUCH_TO_FILL_ONE_TIME_TOKENS_BRIDGE_H_
diff --git a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_bridge_impl.cc b/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_bridge_impl.cc
deleted file mode 100644
index 7fbd1dd..0000000
--- a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_bridge_impl.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_bridge_impl.h"
-
-#include "base/android/jni_string.h"
-#include "base/android/scoped_java_ref.h"
-#include "base/check.h"
-#include "chrome/browser/touch_to_fill/autofill/one_time_tokens/android/internal/jni/TouchToFillOneTimeTokensBridge_jni.h"
-#include "chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_delegate.h"
-#include "content/public/browser/web_contents.h"
-#include "ui/android/view_android.h"
-#include "ui/android/window_android.h"
-
-TouchToFillOneTimeTokensBridgeImpl::TouchToFillOneTimeTokensBridgeImpl() =
-    default;
-
-TouchToFillOneTimeTokensBridgeImpl::~TouchToFillOneTimeTokensBridgeImpl() =
-    default;
-
-bool TouchToFillOneTimeTokensBridgeImpl::Show(
-    content::WebContents* web_contents,
-    TouchToFillOneTimeTokensDelegate* delegate,
-    const std::u16string& token) {
-  if (!web_contents->GetNativeView() ||
-      !web_contents->GetNativeView()->GetWindowAndroid()) {
-    return false;
-  }
-  delegate_ = delegate;
-  JNIEnv* env = base::android::AttachCurrentThread();
-
-  java_bridge_.Reset(Java_TouchToFillOneTimeTokensBridge_create(
-      env, web_contents->GetNativeView()->GetWindowAndroid()->GetJavaObject(),
-      reinterpret_cast<intptr_t>(this)));
-
-  base::android::ScopedJavaLocalRef<jstring> j_token =
-      base::android::ConvertUTF16ToJavaString(env, token);
-
-  return Java_TouchToFillOneTimeTokensBridge_show(env, java_bridge_, j_token);
-}
-
-void TouchToFillOneTimeTokensBridgeImpl::Hide() {
-  if (!java_bridge_) {
-    return;
-  }
-
-  Java_TouchToFillOneTimeTokensBridge_hide(base::android::AttachCurrentThread(),
-                                           java_bridge_);
-}
-
-void TouchToFillOneTimeTokensBridgeImpl::OnDismissed(JNIEnv* env,
-                                                     bool token_accepted) {
-  CHECK(delegate_);
-  delegate_->OnDismissed(token_accepted);
-}
-
-void TouchToFillOneTimeTokensBridgeImpl::OnTokenAccepted(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jstring>& token) {
-  CHECK(delegate_);
-  delegate_->OnTokenAccepted(
-      base::android::ConvertJavaStringToUTF16(env, token));
-}
-
-void TouchToFillOneTimeTokensBridgeImpl::OnTokenRejected(JNIEnv* env) {
-  CHECK(delegate_);
-  delegate_->OnTokenRejected();
-}
diff --git a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_bridge_impl.h b/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_bridge_impl.h
deleted file mode 100644
index e7a9154..0000000
--- a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_bridge_impl.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_TOUCH_TO_FILL_ONE_TIME_TOKENS_BRIDGE_IMPL_H_
-#define CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_TOUCH_TO_FILL_ONE_TIME_TOKENS_BRIDGE_IMPL_H_
-
-#include "base/android/scoped_java_ref.h"
-#include "base/memory/raw_ptr.h"
-#include "chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_bridge.h"
-
-class TouchToFillOneTimeTokensDelegate;
-
-// The implementation of the JNI bridge for the one-time tokens Touch-To-Fill.
-class TouchToFillOneTimeTokensBridgeImpl
-    : public TouchToFillOneTimeTokensBridge {
- public:
-  TouchToFillOneTimeTokensBridgeImpl();
-  ~TouchToFillOneTimeTokensBridgeImpl() override;
-
-  bool Show(content::WebContents* web_contents,
-            TouchToFillOneTimeTokensDelegate* delegate,
-            const std::u16string& token) override;
-
-  void Hide() override;
-
-  // Methods called from Java.
-  void OnDismissed(JNIEnv* env, bool token_accepted);
-  void OnTokenAccepted(JNIEnv* env,
-                       const base::android::JavaParamRef<jstring>& token);
-  void OnTokenRejected(JNIEnv* env);
-
- private:
-  raw_ptr<TouchToFillOneTimeTokensDelegate> delegate_;
-  base::android::ScopedJavaGlobalRef<jobject> java_bridge_;
-};
-
-#endif  // CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_TOUCH_TO_FILL_ONE_TIME_TOKENS_BRIDGE_IMPL_H_
diff --git a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_controller.cc b/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_controller.cc
deleted file mode 100644
index 3985684..0000000
--- a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_controller.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_controller.h"
-
-#include "chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_bridge_impl.h"
-#include "content/public/browser/web_contents.h"
-
-TouchToFillOneTimeTokensController::TouchToFillOneTimeTokensController()
-    : bridge_(std::make_unique<TouchToFillOneTimeTokensBridgeImpl>()) {}
-
-TouchToFillOneTimeTokensController::TouchToFillOneTimeTokensController(
-    std::unique_ptr<TouchToFillOneTimeTokensBridge> bridge)
-    : bridge_(std::move(bridge)) {}
-
-TouchToFillOneTimeTokensController::~TouchToFillOneTimeTokensController() =
-    default;
-
-void TouchToFillOneTimeTokensController::Show(
-    content::WebContents* web_contents,
-    const std::u16string& token) {
-  // TODO(crbug.com/415273777): Implement logic to show the bottom sheet.
-  bridge_->Show(web_contents, this, token);
-}
-
-void TouchToFillOneTimeTokensController::Hide() {
-  bridge_->Hide();
-}
-
-void TouchToFillOneTimeTokensController::OnDismissed(bool token_accepted) {
-  // TODO(crbug.com/415273777): Implement dismissal logic.
-}
-
-void TouchToFillOneTimeTokensController::OnTokenAccepted(
-    const std::u16string& token) {
-  // TODO(crbug.com/415273777): Implement token acceptance logic (e.g., fill the
-  // form).
-}
-
-void TouchToFillOneTimeTokensController::OnTokenRejected() {
-  // TODO(crbug.com/415273777): Implement token rejection logic.
-}
diff --git a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_controller.h b/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_controller.h
deleted file mode 100644
index 140200d..0000000
--- a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_controller.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_TOUCH_TO_FILL_ONE_TIME_TOKENS_CONTROLLER_H_
-#define CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_TOUCH_TO_FILL_ONE_TIME_TOKENS_CONTROLLER_H_
-
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_delegate.h"
-
-namespace content {
-class WebContents;
-}
-
-class TouchToFillOneTimeTokensBridge;
-
-// Controller for the one-time token Touch-To-Fill feature.
-class TouchToFillOneTimeTokensController
-    : public TouchToFillOneTimeTokensDelegate {
- public:
-  TouchToFillOneTimeTokensController();
-  // Constructor for testing, allowing dependency injection.
-  explicit TouchToFillOneTimeTokensController(
-      std::unique_ptr<TouchToFillOneTimeTokensBridge> bridge);
-
-  ~TouchToFillOneTimeTokensController() override;
-
-  // Show the bottom sheet with the one-time token.
-  void Show(content::WebContents* web_contents, const std::u16string& token);
-
-  // TouchToFillOneTimeTokenDelegate:
-  void OnDismissed(bool token_accepted) override;
-  void OnTokenAccepted(const std::u16string& token) override;
-  void OnTokenRejected() override;
-
- private:
-  void Hide();
-  std::unique_ptr<TouchToFillOneTimeTokensBridge> bridge_;
-};
-
-#endif  // CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_TOUCH_TO_FILL_ONE_TIME_TOKENS_CONTROLLER_H_
diff --git a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_controller_unittest.cc b/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_controller_unittest.cc
deleted file mode 100644
index 9f8f8a5..0000000
--- a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_controller_unittest.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_controller.h"
-
-#include "base/test/mock_callback.h"
-#include "chrome/browser/touch_to_fill/autofill/one_time_tokens/android/mock_touch_to_fill_one_time_tokens_bridge.h"
-#include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-using ::testing::Eq;
-
-class TouchToFillOneTimeTokensControllerTest
-    : public ChromeRenderViewHostTestHarness {};
-
-TEST_F(TouchToFillOneTimeTokensControllerTest, ShowCallsBridge) {
-  auto bridge = std::make_unique<MockTouchToFillOneTimeTokensBridge>();
-  MockTouchToFillOneTimeTokensBridge* bridge_ptr = bridge.get();
-
-  TouchToFillOneTimeTokensController controller(std::move(bridge));
-
-  const std::u16string test_token = u"123456";
-  EXPECT_CALL(*bridge_ptr, Show(_, _, Eq(test_token)))
-      .WillOnce(testing::Return(true));
-
-  controller.Show(web_contents(), test_token);
-}
diff --git a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_delegate.h b/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_delegate.h
deleted file mode 100644
index b2497b78..0000000
--- a/chrome/browser/touch_to_fill/autofill/one_time_tokens/android/touch_to_fill_one_time_tokens_delegate.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_TOUCH_TO_FILL_ONE_TIME_TOKENS_DELEGATE_H_
-#define CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_TOUCH_TO_FILL_ONE_TIME_TOKENS_DELEGATE_H_
-
-#include <string>
-
-// This delegate provides a C++ interface for the Java side of the touch to fill
-// one-time tokens UI.
-class TouchToFillOneTimeTokensDelegate {
- public:
-  virtual ~TouchToFillOneTimeTokensDelegate() = default;
-
-  // Called when the bottom sheet is dismissed by the user, either by accepting
-  // or rejecting the token.
-  virtual void OnDismissed(bool token_accepted) = 0;
-
-  // Called when the user accepts the one-time token.
-  virtual void OnTokenAccepted(const std::u16string& token) = 0;
-
-  // Called when the user rejects the one-time token.
-  virtual void OnTokenRejected() = 0;
-};
-
-#endif  // CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ONE_TIME_TOKENS_ANDROID_TOUCH_TO_FILL_ONE_TIME_TOKENS_DELEGATE_H_
diff --git a/chrome/browser/tpcd/support/origin_trial_service.cc b/chrome/browser/tpcd/support/origin_trial_service.cc
deleted file mode 100644
index 0ec40e7..0000000
--- a/chrome/browser/tpcd/support/origin_trial_service.cc
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/tpcd/support/origin_trial_service.h"
-
-#include "base/metrics/histogram_functions.h"
-#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/document_user_data.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/origin_trials_controller_delegate.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/storage_partition.h"
-#include "net/base/features.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-#include "services/metrics/public/cpp/ukm_recorder.h"
-#include "services/network/public/mojom/cookie_manager.mojom.h"
-
-namespace tpcd::trial {
-namespace {
-
-const char kTrialName[] = "LimitThirdPartyCookies";
-
-bool IsSameSite(const GURL& url1, const GURL& url2) {
-  // We can't use SiteInstance::IsSameSiteWithURL() because both mainframe and
-  // subframe are under default SiteInstance on low-end Android environment, and
-  // it treats them as same-site even though the passed url is actually not a
-  // same-site.
-  return url1.SchemeIs(url2.GetScheme()) &&
-         net::registry_controlled_domains::SameDomainOrHost(
-             url1, url2,
-             net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
-}
-}  // namespace
-
-OriginTrialService::OriginTrialService(content::BrowserContext* browser_context)
-    : browser_context_(browser_context) {
-  ot_controller_ = browser_context->GetOriginTrialsControllerDelegate();
-  if (ot_controller_) {
-    ot_controller_->AddObserver(this);
-  }
-}
-
-OriginTrialService::~OriginTrialService() = default;
-
-void OriginTrialService::Shutdown() {
-  if (ot_controller_) {
-    ot_controller_->RemoveObserver(this);
-  }
-
-  ot_controller_ = nullptr;
-  browser_context_ = nullptr;
-}
-
-void OriginTrialService::UpdateTopLevelTrialSettingsForTesting(
-    const url::Origin& origin,
-    bool match_subdomains,
-    bool enabled) {
-  UpdateTopLevelTrialSettings(origin, match_subdomains, enabled);
-}
-
-void OriginTrialService::UpdateTopLevelTrialSettings(const url::Origin& origin,
-                                                     bool match_subdomains,
-                                                     bool enabled) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  const GURL origin_as_url = origin.GetURL();
-
-  ContentSettingsPattern primary_setting_pattern;
-  if (match_subdomains) {
-    // `ContentSettingsPattern::FromURL()` returns a pattern with a domain
-    // wildcard by default.
-    primary_setting_pattern = ContentSettingsPattern::FromURL(origin_as_url);
-  } else {
-    primary_setting_pattern =
-        ContentSettingsPattern::FromURLNoWildcard(origin_as_url);
-  }
-
-  HostContentSettingsMap* settings_map =
-      HostContentSettingsMapFactory::GetForProfile(browser_context_);
-  CHECK(settings_map);
-
-  // Check for an existing `TOP_LEVEL_TPCD_ORIGIN_TRIAL` setting that allows
-  // `origin`. Only the primary pattern is checked since the secondary pattern
-  // is unused (i.e., wildcard) for this content settings type's scope
-  // (`TOP_ORIGIN_ONLY_SCOPE`).
-  content_settings::SettingInfo existing_setting_info;
-  bool setting_exists =
-      (settings_map->GetContentSetting(
-           origin_as_url, GURL(),
-           ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL,
-           &existing_setting_info) == CONTENT_SETTING_BLOCK) &&
-      (existing_setting_info.primary_pattern == primary_setting_pattern);
-
-  // If the trial status matches existing settings, there is no need to
-  // update `settings_map`.
-  if (enabled == setting_exists) {
-    return;
-  }
-
-  if (enabled) {
-    settings_map->SetContentSettingCustomScope(
-        primary_setting_pattern, ContentSettingsPattern::Wildcard(),
-        ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL,
-        CONTENT_SETTING_BLOCK);
-  } else {
-    CHECK(setting_exists);
-
-    // The trial has been disabled for origin, so remove the associated setting.
-    auto matches_pattern =
-        [&](const ContentSettingPatternSource& setting) -> bool {
-      // Only check the primary_pattern since the settings type uses
-      // `TOP_ORIGIN_ONLY_SCOPE`.
-      return (setting.primary_pattern == existing_setting_info.primary_pattern);
-    };
-
-    settings_map->ClearSettingsForOneTypeWithPredicate(
-        ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL, matches_pattern);
-  }
-
-  ContentSettingsForOneType trial_settings =
-      settings_map->GetSettingsForOneType(
-          ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL);
-
-  browser_context_->GetDefaultStoragePartition()
-      ->GetCookieManagerForBrowserProcess()
-      ->SetContentSettings(ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL,
-                           std::move(trial_settings), base::NullCallback());
-}
-
-void OriginTrialService::ClearTopLevelTrialSettings() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  HostContentSettingsMap* settings_map =
-      HostContentSettingsMapFactory::GetForProfile(browser_context_);
-  CHECK(settings_map);
-
-  settings_map->ClearSettingsForOneType(
-      ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL);
-}
-
-void OriginTrialService::OnStatusChanged(
-    const content::OriginTrialStatusChangeDetails& details) {
-  // LimitThirdPartyCookies is a first-party trial that is only intended for use
-  // by top-level sites. However, a cross-site iframe may enable a first-party
-  // origin trial (for itself), so to ensure we only create settings for
-  // top-level sites, explicitly check that `origin` is same-site with
-  // `partition_site`.
-  if (!IsSameSite(details.origin.GetURL(), GURL(details.partition_site))) {
-    return;
-  }
-
-  UpdateTopLevelTrialSettings(details.origin, details.match_subdomains,
-                              details.enabled);
-}
-
-void OriginTrialService::OnPersistedTokensCleared() {
-  ClearTopLevelTrialSettings();
-}
-
-std::string OriginTrialService::trial_name() {
-  return kTrialName;
-}
-
-}  // namespace tpcd::trial
diff --git a/chrome/browser/tpcd/support/origin_trial_service.h b/chrome/browser/tpcd/support/origin_trial_service.h
deleted file mode 100644
index ef2b8e0..0000000
--- a/chrome/browser/tpcd/support/origin_trial_service.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_TPCD_SUPPORT_ORIGIN_TRIAL_SERVICE_H_
-#define CHROME_BROWSER_TPCD_SUPPORT_ORIGIN_TRIAL_SERVICE_H_
-
-#include "base/memory/raw_ptr.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "content/public/browser/origin_trials_controller_delegate.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
-
-namespace tpcd::trial {
-
-// A profile-keyed service that maintains TOP_LEVEL_TPCD_ORIGIN_TRIAL content
-// settings based on the state of the associated origin trial for a given
-// `origin`.
-class OriginTrialService
-    : public content::OriginTrialsControllerDelegate::Observer,
-      public KeyedService {
- public:
-  explicit OriginTrialService(content::BrowserContext* browser_context);
-  ~OriginTrialService() override;
-  OriginTrialService(const OriginTrialService&) = delete;
-  OriginTrialService& operator=(const OriginTrialService&) = delete;
-
-  // KeyedService overrides:
-  void Shutdown() override;
-
-  void UpdateTopLevelTrialSettingsForTesting(const url::Origin& origin,
-                                             bool match_subdomains,
-                                             bool enabled);
-
- private:
-  // Updates `ContentSettingsForOneType::TOP_LEVEL_TPCD_ORIGIN_TRIAL` to reflect
-  // the status of the trial for `origin`. If `match_subdomains` is true,
-  // a custom scope is used for the content setting to match all subdomains of
-  // `origin`.
-  void UpdateTopLevelTrialSettings(const url::Origin& origin,
-                                   bool match_subdomains,
-                                   bool enabled);
-  void ClearTopLevelTrialSettings();
-
-  // content::OriginTrialsControllerDelegate::Observer overrides:
-  void OnStatusChanged(
-      const content::OriginTrialStatusChangeDetails& details) override;
-  void OnPersistedTokensCleared() override;
-  std::string trial_name() override;
-
-  raw_ptr<content::OriginTrialsControllerDelegate> ot_controller_;
-  raw_ptr<content::BrowserContext> browser_context_;
-};
-
-}  // namespace tpcd::trial
-
-#endif  // CHROME_BROWSER_TPCD_SUPPORT_ORIGIN_TRIAL_SERVICE_H_
diff --git a/chrome/browser/tpcd/support/origin_trial_service_browsertest.cc b/chrome/browser/tpcd/support/origin_trial_service_browsertest.cc
deleted file mode 100644
index 3f4e4f4d..0000000
--- a/chrome/browser/tpcd/support/origin_trial_service_browsertest.cc
+++ /dev/null
@@ -1,579 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/tpcd/support/origin_trial_service.h"
-
-#include "base/strings/strcat.h"
-#include "base/test/bind.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/content_settings/cookie_settings_factory.h"
-#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/tpcd/support/trial_test_utils.h"
-#include "chrome/test/base/chrome_test_utils.h"
-#include "chrome/test/base/platform_browser_test.h"
-#include "components/content_settings/core/browser/content_settings_observer.h"
-#include "components/content_settings/core/browser/cookie_settings.h"
-#include "components/content_settings/core/common/content_settings.h"
-#include "components/content_settings/core/common/content_settings_pattern.h"
-#include "components/content_settings/core/common/content_settings_types.h"
-#include "components/content_settings/core/common/features.h"
-#include "components/content_settings/core/common/pref_names.h"
-#include "components/network_session_configurator/common/network_switches.h"
-#include "components/prefs/pref_service.h"
-#include "components/privacy_sandbox/tracking_protection_prefs.h"
-#include "components/user_prefs/user_prefs.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/common/content_features.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/url_loader_interceptor.h"
-#include "net/base/features.h"
-#include "net/cookies/cookie_util.h"
-#include "net/dns/mock_host_resolver.h"
-
-using content::URLLoaderInterceptor;
-using content::WebContents;
-
-namespace tpcd::trial {
-
-namespace {
-// Origin Trials token for `kTrialEnabledDomain` generated with:
-// tools/origin_trials/generate_token.py  https://example.test
-// LimitThirdPartyCookies --expire-days 1000
-const char k1pOriginTrialToken[] =
-    "A4rXpaazyMmgJ1ZFM0VTbJVkN6MUjDmBpTgPcW8Eo4NJ1HREVo0k7+C3+kG15uIMu/"
-    "4g7bjRt9oD+"
-    "EiiWa7Pqg8AAABheyJvcmlnaW4iOiAiaHR0cHM6Ly9leGFtcGxlLnRlc3Q6NDQzIiwgImZlYXR"
-    "1cmUiOiAiTGltaXRUaGlyZFBhcnR5Q29va2llcyIsICJleHBpcnkiOiAxODA0MTg0MjE4fQ==";
-
-// Origin Trials token for `kTrialEnabledDomain` (and all its subdomains)
-// generated with:
-// tools/origin_trials/generate_token.py https://example.test
-// LimitThirdPartyCookies --is-subdomain --expire-days 1000
-const char k1pOriginTrialSubdomainMatchingToken[] =
-    "A+"
-    "NeirFywr04ZJyST5qNSP5gHYkbsvrJhd2DZrmgqxtqcobc89BaLAiLzV51Pmf73gaSozjBljgg"
-    "eqecVcuE/"
-    "Q4AAAB2eyJvcmlnaW4iOiAiaHR0cHM6Ly9leGFtcGxlLnRlc3Q6NDQzIiwgImZlYXR1cmUiOiA"
-    "iTGltaXRUaGlyZFBhcnR5Q29va2llcyIsICJleHBpcnkiOiAxODA0MTg0MTY4LCAiaXNTdWJkb"
-    "21haW4iOiB0cnVlfQ==";
-
-// Origin Trials token for `kTrialEnabledSiteSubdomain` generated with:
-// tools/origin_trials/generate_token.py  https://sub.example.test
-// LimitThirdPartyCookies --expire-days 1000
-const char kSubdomain1pOriginTrialToken[] =
-    "A79E1oRwWqh+tVNJ1LalYnrfd9DZOhYmj6V85KXkiYeFbC97V1Bg6FPq+"
-    "lxMNrIY5hDazYMBVkyGC/"
-    "sdVstIeQAAAABleyJvcmlnaW4iOiAiaHR0cHM6Ly9zdWIuZXhhbXBsZS50ZXN0OjQ0MyIsICJm"
-    "ZWF0dXJlIjogIkxpbWl0VGhpcmRQYXJ0eUNvb2tpZXMiLCAiZXhwaXJ5IjogMTgwNDE4OTQyN3"
-    "0=";
-
-// Origin Trials token for `kTrialEnabledSiteSubdomain` (and all its subdomains)
-// generated with:
-// tools/origin_trials/generate_token.py https://sub.example.test
-// LimitThirdPartyCookies --is-subdomain --expire-days 1000
-const char kSubdomain1pOriginTrialSubdomainMatchingToken[] =
-    "AzosDlpG4yrKAm0NVvtYqeO7yRjli5018CiXPMGxsFIMrBpYFvAYlGQgg8/"
-    "yhs5H5WvrUrvnjbXvHK+"
-    "b28bccgUAAAB6eyJvcmlnaW4iOiAiaHR0cHM6Ly9zdWIuZXhhbXBsZS50ZXN0OjQ0MyIsICJmZ"
-    "WF0dXJlIjogIkxpbWl0VGhpcmRQYXJ0eUNvb2tpZXMiLCAiZXhwaXJ5IjogMTgwNDE4OTQ2NCw"
-    "gImlzU3ViZG9tYWluIjogdHJ1ZX0=";
-
-// Origin Trials token for `kOtherTrialEnabledDomain` generated with:
-// tools/origin_trials/generate_token.py  https://other.test
-// LimitThirdPartyCookies --expire-days 1000
-const char kOtherDomain1pOriginTrialToken[] =
-    "A9/u+zN/DMWbxg0fafsYuMc7svS/X8qGRcSeehuA886viCDAJQea/"
-    "GDAOLaTgA2C0UD98mOkmd1Qi2oiYXb2SAcAAABfeyJvcmlnaW4iOiAiaHR0cHM6Ly9vdGhlci5"
-    "0ZXN0OjQ0MyIsICJmZWF0dXJlIjogIkxpbWl0VGhpcmRQYXJ0eUNvb2tpZXMiLCAiZXhwaXJ5I"
-    "jogMTgwNDE4OTU0Mn0=";
-
-}  // namespace
-
-class TpcdOriginTrialBrowserTest : public PlatformBrowserTest {
- public:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII("origin-trial-public-key",
-                                    kTestTokenPublicKey);
-    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
-  }
-
-  void SetUp() override {
-    features_.InitWithFeaturesAndParameters(
-        {{net::features::kTopLevelTpcdOriginTrial, {}}},
-        {content_settings::features::kTrackingProtection3pcd});
-
-    PlatformBrowserTest::SetUp();
-  }
-
-  void SetUpOnMainThread() override {
-    host_resolver()->AddRule("*", "127.0.0.1");
-
-    https_server_ = std::make_unique<net::EmbeddedTestServer>(
-        net::EmbeddedTestServer::TYPE_HTTPS);
-    https_server_->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
-    https_server_->AddDefaultHandlers(
-        base::FilePath(FILE_PATH_LITERAL("chrome/test/data/")));
-    ASSERT_TRUE(https_server_->Start());
-
-    // We use a URLLoaderInterceptor, rather than the EmbeddedTestServer, since
-    // the origin trial token in the response is associated with a fixed
-    // origin, whereas EmbeddedTestServer serves content on a random port.
-    url_loader_interceptor_ =
-        std::make_unique<URLLoaderInterceptor>(base::BindLambdaForTesting(
-            [this](URLLoaderInterceptor::RequestParams* params) {
-              return OnRequest(params);
-            }));
-
-    // GetPrefs()->SetBoolean(prefs::kTrackingProtection3pcdEnabled, false);
-  }
-
-  void TearDownOnMainThread() override {
-    https_server_.reset();
-    url_loader_interceptor_.reset();
-    PlatformBrowserTest::TearDownOnMainThread();
-  }
-
- protected:
-  content::WebContents* GetActiveWebContents() {
-    return chrome_test_utils::GetActiveWebContents(this);
-  }
-
-  Profile* GetProfile() {
-    return Profile::FromBrowserContext(
-        GetActiveWebContents()->GetBrowserContext());
-  }
-
-  PrefService* GetPrefs() {
-    return user_prefs::UserPrefs::Get(
-        GetActiveWebContents()->GetBrowserContext());
-  }
-
-  // Most other cookie-related content settings compare their primary patterns'
-  // against embedded/requesting sites and their secondary patterns'
-  // against top-level sites. This convenience function helps avoid confusion
-  // since `TOP_LEVEL_TPCD_ORIGIN_TRIAL` content settings only use a primary
-  // pattern which is compared against top-level sites.
-  ContentSettingChangeObserver CreateTpcdOriginTrialSettingsObserver(GURL url) {
-    return ContentSettingChangeObserver(
-        GetProfile(), url, GURL(),
-        ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL);
-  }
-
-  bool OnRequest(content::URLLoaderInterceptor::RequestParams* params) {
-    std::string host = params->url_request.url.GetHost();
-    std::string path = params->url_request.url.GetPath().substr(1);
-    std::string query = params->url_request.url.GetQuery();
-
-    if (host != kTrialEnabledDomain && host != kTrialEnabledSubdomain &&
-        host != kOtherTrialEnabledDomain) {
-      return false;
-    }
-
-    std::string headers =
-        "HTTP/1.1 200 OK\n"
-        "Content-type: text/html\n";
-    std::string body = "";
-
-    std::string token = "";
-    if (host == kTrialEnabledDomain) {
-      if (query == "subdomain_matching_token") {
-        token = k1pOriginTrialSubdomainMatchingToken;
-      } else if (query != "no_token") {
-        token = k1pOriginTrialToken;
-      }
-    }
-
-    if (host == kTrialEnabledSubdomain) {
-      if (query == "etld_plus_1_token") {
-        token = k1pOriginTrialSubdomainMatchingToken;
-      } else if (query == "subdomain_matching_token") {
-        token = kSubdomain1pOriginTrialSubdomainMatchingToken;
-      } else if (query != "no_token") {
-        token = kSubdomain1pOriginTrialToken;
-      }
-    }
-
-    if (host == kOtherTrialEnabledDomain) {
-      if (query != "no_token") {
-        token = kOtherDomain1pOriginTrialToken;
-      }
-    }
-
-    if (!token.empty()) {
-      if (path.find("meta_tag") == 0) {
-        body =
-            "<html>\n"
-            "<head>\n"
-            "<meta http-equiv='origin-trial' "
-            "content='" +
-            token +
-            "'>\n"
-            "</head>\n"
-            "<body></body>\n"
-            "</html>\n";
-      } else {
-        base::StrAppend(&headers, {"Origin-Trial: ", token, "\n"});
-      }
-    }
-
-    content::URLLoaderInterceptor::WriteResponse(headers, body,
-                                                 params->client.get());
-    return true;
-  }
-
-  base::test::ScopedFeatureList features_;
-  std::unique_ptr<net::EmbeddedTestServer> https_server_;
-  std::unique_ptr<URLLoaderInterceptor> url_loader_interceptor_;
-  const GURL kTrialEnabledSite{base::StrCat({"https://", kTrialEnabledDomain})};
-  const GURL kTrialEnabledSiteSubdomain{
-      base::StrCat({"https://", kTrialEnabledSubdomain})};
-  const GURL kOtherTrialEnabledSite{
-      base::StrCat({"https://", kOtherTrialEnabledDomain})};
-};
-
-IN_PROC_BROWSER_TEST_F(TpcdOriginTrialBrowserTest, EnabledAfterHttpResponse) {
-  content::WebContents* web_contents = GetActiveWebContents();
-
-  // Verify third-party cookie is allowed under `kTrialEnabledSite`.
-  content_settings::CookieSettings* settings =
-      CookieSettingsFactory::GetForProfile(GetProfile()).get();
-  ASSERT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_ALLOW);
-
-  // Navigate to a `kTrialEnabledSite` page that returns its origin trial token
-  // in the HTTP response headers.
-  {
-    ContentSettingChangeObserver setting_observer =
-        CreateTpcdOriginTrialSettingsObserver(kTrialEnabledSite);
-    ASSERT_TRUE(content::NavigateToURL(web_contents, kTrialEnabledSite));
-    setting_observer.Wait();
-  }
-
-  // Check that third-party cookie access is blocked under
-  // `kTrialEnabledSite`.
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_BLOCK);
-
-  // Verify that a subsequent load of a `kTrialEnabledSite` page
-  // without the token removes the `TOP_LEVEL_TPCD_ORIGIN_TRIAL` content setting
-  // for it.
-  {
-    GURL enabled_site_no_token = GURL(kTrialEnabledSite.spec() + "?no_token");
-    ContentSettingChangeObserver setting_observer =
-        CreateTpcdOriginTrialSettingsObserver(enabled_site_no_token);
-    ASSERT_TRUE(content::NavigateToURL(web_contents, enabled_site_no_token));
-    setting_observer.Wait();
-  }
-
-  // Verify third-party cookie access is no longer blocked under
-  // `kTrialEnabledSite`.
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_ALLOW);
-}
-
-IN_PROC_BROWSER_TEST_F(TpcdOriginTrialBrowserTest,
-                       EnabledAfterHttpResponseWithEtldSubdomainMatchingToken) {
-  content::WebContents* web_contents = GetActiveWebContents();
-
-  // Verify third-party cookie is allowed under `kTrialEnabledSite`
-  // and `kTrialEnabledSiteSubdomain`.
-  content_settings::CookieSettings* settings =
-      CookieSettingsFactory::GetForProfile(GetProfile()).get();
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_ALLOW);
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSiteSubdomain, {}, nullptr),
-            CONTENT_SETTING_ALLOW);
-
-  // Navigate to a `kTrialEnabledSiteSubdomain` page that returns the subdomain
-  // matching origin trial token for it's eTLD+1 (`kTrialEnabledSite`) in the
-  // HTTP response headers.
-  {
-    GURL url = GURL(kTrialEnabledSiteSubdomain.spec() + "?etld_plus_1_token");
-    ContentSettingChangeObserver setting_observer =
-        CreateTpcdOriginTrialSettingsObserver(url);
-    ASSERT_TRUE(content::NavigateToURL(web_contents, url));
-    setting_observer.Wait();
-  }
-
-  // Check that third-party cookie access is blocked  under
-  // `kTrialEnabledSite` and `kTrialEnabledSiteSubdomain`.
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSiteSubdomain, {}, nullptr),
-            CONTENT_SETTING_BLOCK);
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_BLOCK);
-
-  // Verify that a subsequent load of a page from `kTrialEnabledSiteSubdomain`'s
-  // eTLD+1 (`kTrialEnabledSite`) without the token removes the
-  // `TOP_LEVEL_TPCD_ORIGIN_TRIAL` content setting for them.
-  {
-    GURL enabled_site_no_token = GURL(kTrialEnabledSite.spec() + "?no_token");
-    ContentSettingChangeObserver setting_observer =
-        CreateTpcdOriginTrialSettingsObserver(enabled_site_no_token);
-    ASSERT_TRUE(content::NavigateToURL(web_contents, enabled_site_no_token));
-    setting_observer.Wait();
-  }
-
-  // Verify third-party cookie access is no longer blocked under
-  // `kTrialEnabledSite` or `kTrialEnabledSiteSubdomain`.
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSiteSubdomain, {}, nullptr),
-            CONTENT_SETTING_ALLOW);
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_ALLOW);
-}
-
-IN_PROC_BROWSER_TEST_F(TpcdOriginTrialBrowserTest, EnabledUsingMetaTag) {
-  content::WebContents* web_contents = GetActiveWebContents();
-
-  // Navigate to a `kTrialEnabledSite` page where its origin trial token is in a
-  // meta tag in the head of the document.
-  {
-    GURL url = GURL(kTrialEnabledSite.spec() + "meta_tag");
-
-    ContentSettingChangeObserver setting_observer =
-        CreateTpcdOriginTrialSettingsObserver(url);
-    ASSERT_TRUE(content::NavigateToURL(web_contents, url));
-    setting_observer.Wait();
-  }
-
-  // Check that third-party cookie access is now blocked under
-  // `kTrialEnabledSite`.
-  content_settings::CookieSettings* settings =
-      CookieSettingsFactory::GetForProfile(GetProfile()).get();
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_BLOCK);
-}
-
-IN_PROC_BROWSER_TEST_F(TpcdOriginTrialBrowserTest,
-                       EnabledUsingMetaTagWithEtldSubdomainMatchingToken) {
-  content::WebContents* web_contents = GetActiveWebContents();
-
-  // Navigate to a `kTrialEnabledSiteSubdomain` page where the subdomain
-  // matching origin trial token for it's eTLD+1 (`kTrialEnabledSite`) is in a
-  // meta tag in the head of the document.
-  {
-    GURL url =
-        GURL(kTrialEnabledSiteSubdomain.spec() + "meta_tag?etld_plus_1_token");
-    ContentSettingChangeObserver setting_observer =
-        CreateTpcdOriginTrialSettingsObserver(url);
-    ASSERT_TRUE(content::NavigateToURL(web_contents, url));
-    setting_observer.Wait();
-  }
-
-  // Verify that third-party cookie access is now blocked under
-  // `kTrialEnabledSite` and `kTrialEnabledSiteSubdomain`.
-  content_settings::CookieSettings* settings =
-      CookieSettingsFactory::GetForProfile(GetProfile()).get();
-
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSiteSubdomain, {}, nullptr),
-            CONTENT_SETTING_BLOCK);
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_BLOCK);
-}
-
-// This test verifies (when enabled using a subdomain matching token) the trial
-// is only disabled if a document from the token origin is loaded, even if one
-// of its subdomains originally enabled the trial.
-IN_PROC_BROWSER_TEST_F(TpcdOriginTrialBrowserTest,
-                       OnlyTokenOriginCanDisableTrial) {
-  content::WebContents* web_contents = GetActiveWebContents();
-
-  // Navigate to a `kTrialEnabledSiteSubdomain` page that returns the subdomain
-  // matching origin trial token for it's eTLD+1 (`kTrialEnabledSite`) in the
-  // HTTP response headers.
-  {
-    GURL url = GURL(kTrialEnabledSiteSubdomain.spec() + "?etld_plus_1_token");
-    ContentSettingChangeObserver setting_observer =
-        CreateTpcdOriginTrialSettingsObserver(url);
-    ASSERT_TRUE(content::NavigateToURL(web_contents, url));
-    setting_observer.Wait();
-  }
-
-  // Check that third-party cookie access is blocked under `kTrialEnabledSite`
-  // and `kTrialEnabledSiteSubdomain`.
-  content_settings::CookieSettings* settings =
-      CookieSettingsFactory::GetForProfile(GetProfile()).get();
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_BLOCK);
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSiteSubdomain, {}, nullptr),
-            CONTENT_SETTING_BLOCK);
-
-  // Verify that subsequent loads of a pages from
-  // `kTrialEnabledSiteSubdomain` and other subdomains of the token origin
-  // (`kTrialEnabledSite`) without the token do not affect the
-  // `TOP_LEVEL_TPCD_ORIGIN_TRIAL` content setting for them.
-  {
-    GURL enabled_site_subdomain_no_token =
-        GURL(kTrialEnabledSiteSubdomain.spec() + "?no_token");
-    ASSERT_TRUE(
-        content::NavigateToURL(web_contents, enabled_site_subdomain_no_token));
-
-    GURL other_subdomain_no_token = https_server_->GetURL(
-        base::StrCat({"random-subdomain.", kTrialEnabledDomain}),
-        "/title1.html");
-    ASSERT_TRUE(content::NavigateToURL(web_contents, other_subdomain_no_token));
-  }
-
-  // Since we can't deterministically wait for the OriginTrialService to do
-  // nothing in response to page loads that shouldn't affect trial state,
-  // navigate to a page that enables the trial for a different origin and wait
-  // for the associated `TOP_LEVEL_TPCD_ORIGIN_TRIAL` settings to be created,
-  // then check that the subdomain matching content setting for
-  // `kTrialEnabledSite` still remains.
-  {
-    ContentSettingChangeObserver setting_observer =
-        CreateTpcdOriginTrialSettingsObserver(kOtherTrialEnabledSite);
-    ASSERT_TRUE(content::NavigateToURL(web_contents, kOtherTrialEnabledSite));
-    setting_observer.Wait();
-  }
-
-  // Check that third-party cookie access is still blocked under
-  // `kTrialEnabledSite` and `kTrialEnabledSiteSubdomain`.
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_BLOCK);
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSiteSubdomain, {}, nullptr),
-            CONTENT_SETTING_BLOCK);
-}
-
-IN_PROC_BROWSER_TEST_F(TpcdOriginTrialBrowserTest,
-                       NoSettingCreatedIfTrialEnabledCrossSite) {
-  base::HistogramTester histograms;
-  content::WebContents* web_contents = GetActiveWebContents();
-  GURL embedding_site = https_server_->GetURL("a.test", "/iframe_blank.html");
-
-  // Verify third-party cookie access is allowed under `kTrialEnabledSite`.
-  content_settings::CookieSettings* settings =
-      CookieSettingsFactory::GetForProfile(GetProfile()).get();
-  ASSERT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_ALLOW);
-
-  // Navigate the top-level page to `embedding_site` and update it to have an
-  // `kTrialEnabledSite` iframe that returns its LimitThirdPartyCookies origin
-  // trial token in its HTTP response headers.
-  {
-    ASSERT_TRUE(content::NavigateToURL(web_contents, embedding_site));
-    const std::string kIframeId = "test";  // defined in iframe_blank.html
-    GURL iframe_url = GURL(kTrialEnabledSite.spec() + kTrialEnabledIframePath);
-    ASSERT_TRUE(
-        content::NavigateIframeToURL(web_contents, kIframeId, iframe_url));
-  }
-
-  // Check the LimitThirdPartyCookies origin trial itself is enabled for
-  // `kTrialEnabledSite` embedded under `embedding_site`.
-  content::OriginTrialsControllerDelegate* trial_delegate =
-      web_contents->GetBrowserContext()->GetOriginTrialsControllerDelegate();
-
-  EXPECT_TRUE(trial_delegate->IsFeaturePersistedForOrigin(
-      url::Origin::Create(kTrialEnabledSite),
-      url::Origin::Create(embedding_site),
-      blink::mojom::OriginTrialFeature::kLimitThirdPartyCookies,
-      base::Time::Now()));
-
-  // Verify that third-party cookie access is still allowed under
-  // `kTrialEnabledSite`.
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_ALLOW);
-}
-
-// Since the LimitThirdPartyCookies origin trial itself can be enabled/disabled
-// in a cross-site context despite the trial only being intended to support
-// top-level sites, this test verifies that changes to the status of the trial
-// for an origin when in a cross-site context doesn't affect an existing content
-// setting created for it in a top-level context.
-IN_PROC_BROWSER_TEST_F(TpcdOriginTrialBrowserTest,
-                       CrossSiteOriginTrialStateChangesIgnored) {
-  base::HistogramTester histograms;
-  content::WebContents* web_contents = GetActiveWebContents();
-  content_settings::CookieSettings* settings =
-      CookieSettingsFactory::GetForProfile(GetProfile()).get();
-
-  // Verify third-party cookie access is already allowed under
-  // `kTrialEnabledSite`.
-  ASSERT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_ALLOW);
-
-  // Enable the trial by navigating to a `kTrialEnabledSite` page that returns
-  // its origin trial token in the HTTP response headers.
-  {
-    ContentSettingChangeObserver setting_observer =
-        CreateTpcdOriginTrialSettingsObserver(kTrialEnabledSite);
-    ASSERT_TRUE(content::NavigateToURL(web_contents, kTrialEnabledSite));
-    setting_observer.Wait();
-    EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                         kTrialEnabledSite, {}, nullptr),
-              CONTENT_SETTING_BLOCK);
-  }
-
-  GURL embedding_site = https_server_->GetURL("a.test", "/iframe_blank.html");
-  const std::string kIframeId = "test";  // defined in iframe_blank.html
-
-  // Enable the origin trial for `kTrialEnabledSite` (under `embedding_site`)
-  // using a cross-site iframe embedded on `embedding_site`.
-  {
-    ASSERT_TRUE(content::NavigateToURL(web_contents, embedding_site));
-
-    GURL iframe_url = GURL(kTrialEnabledSite.spec() + kTrialEnabledIframePath);
-    ASSERT_TRUE(
-        content::NavigateIframeToURL(web_contents, kIframeId, iframe_url));
-  }
-
-  // Check the LimitThirdPartyCookies origin trial itself is enabled for
-  // `kTrialEnabledSite` embedded under `embedding_site`.
-  content::OriginTrialsControllerDelegate* trial_delegate =
-      web_contents->GetBrowserContext()->GetOriginTrialsControllerDelegate();
-
-  EXPECT_TRUE(trial_delegate->IsFeaturePersistedForOrigin(
-      url::Origin::Create(kTrialEnabledSite),
-      url::Origin::Create(embedding_site),
-      blink::mojom::OriginTrialFeature::kLimitThirdPartyCookies,
-      base::Time::Now()));
-
-  // Navigate the iframe to a `kTrialEnabledSite` page without the token to
-  // disable the origin trial for it (under `embedding_site`).
-  {
-    GURL iframe_url = GURL(kTrialEnabledSite.spec() + "?no_token");
-    ASSERT_TRUE(
-        content::NavigateIframeToURL(web_contents, kIframeId, iframe_url));
-  }
-
-  // Check the LimitThirdPartyCookies origin trial itself is now disabled for
-  // `kTrialEnabledSite` embedded under `embedding_site`.
-  EXPECT_FALSE(trial_delegate->IsFeaturePersistedForOrigin(
-      url::Origin::Create(kTrialEnabledSite),
-      url::Origin::Create(embedding_site),
-      blink::mojom::OriginTrialFeature::kLimitThirdPartyCookies,
-      base::Time::Now()));
-
-  // Verify that third-party cookie access is still blocked under
-  // `kTrialEnabledSite`.
-  EXPECT_EQ(settings->GetCookieSetting(GURL(), net::SiteForCookies(),
-                                       kTrialEnabledSite, {}, nullptr),
-            CONTENT_SETTING_BLOCK);
-}
-
-}  // namespace tpcd::trial
diff --git a/chrome/browser/tpcd/support/origin_trial_service_factory.cc b/chrome/browser/tpcd/support/origin_trial_service_factory.cc
deleted file mode 100644
index bfd1677..0000000
--- a/chrome/browser/tpcd/support/origin_trial_service_factory.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/tpcd/support/origin_trial_service_factory.h"
-
-#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/origin_trials/origin_trials_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/tpcd/support/origin_trial_service.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/common/content_features.h"
-#include "net/base/features.h"
-
-namespace tpcd::trial {
-
-// static
-OriginTrialServiceFactory* OriginTrialServiceFactory::GetInstance() {
-  static base::NoDestructor<OriginTrialServiceFactory> factory;
-  return factory.get();
-}
-
-// static
-OriginTrialService* OriginTrialServiceFactory::GetForProfile(Profile* profile) {
-  return static_cast<OriginTrialService*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
-}
-
-// static
-ProfileSelections OriginTrialServiceFactory::CreateProfileSelections() {
-  if (!base::FeatureList::IsEnabled(net::features::kTopLevelTpcdOriginTrial)) {
-    return ProfileSelections::BuildNoProfilesSelected();
-  }
-
-  return ProfileSelections::Builder()
-      .WithRegular(ProfileSelection::kOriginalOnly)
-      .WithGuest(ProfileSelection::kOwnInstance)
-      // The Following will be completely unselected as users do not "browse"
-      // within these profiles.
-      .WithSystem(ProfileSelection::kNone)
-      .WithAshInternals(ProfileSelection::kNone)
-      .Build();
-}
-
-OriginTrialServiceFactory::OriginTrialServiceFactory()
-    : ProfileKeyedServiceFactory("OriginTrialService",
-                                 CreateProfileSelections()) {
-  DependsOn(OriginTrialsFactory::GetInstance());
-  DependsOn(HostContentSettingsMapFactory::GetInstance());
-}
-
-OriginTrialServiceFactory::~OriginTrialServiceFactory() = default;
-
-std::unique_ptr<KeyedService>
-OriginTrialServiceFactory::BuildServiceInstanceForBrowserContext(
-    content::BrowserContext* context) const {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return std::make_unique<OriginTrialService>(context);
-}
-
-}  // namespace tpcd::trial
diff --git a/chrome/browser/tpcd/support/origin_trial_service_factory.h b/chrome/browser/tpcd/support/origin_trial_service_factory.h
deleted file mode 100644
index 60d972c..0000000
--- a/chrome/browser/tpcd/support/origin_trial_service_factory.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_TPCD_SUPPORT_ORIGIN_TRIAL_SERVICE_FACTORY_H_
-#define CHROME_BROWSER_TPCD_SUPPORT_ORIGIN_TRIAL_SERVICE_FACTORY_H_
-
-#include "base/no_destructor.h"
-#include "chrome/browser/profiles/profile_keyed_service_factory.h"
-
-namespace content {
-class BrowserContext;
-}
-
-namespace tpcd::trial {
-class OriginTrialService;
-
-class OriginTrialServiceFactory : public ProfileKeyedServiceFactory {
- public:
-  static OriginTrialServiceFactory* GetInstance();
-  static OriginTrialService* GetForProfile(Profile* profile);
-  static ProfileSelections CreateProfileSelections();
-
-  OriginTrialServiceFactory(const OriginTrialServiceFactory&) = delete;
-  OriginTrialServiceFactory& operator=(const OriginTrialServiceFactory&) =
-      delete;
-  OriginTrialServiceFactory(OriginTrialServiceFactory&&) = delete;
-  OriginTrialServiceFactory& operator=(OriginTrialServiceFactory&&) = delete;
-
- private:
-  friend class base::NoDestructor<OriginTrialServiceFactory>;
-
-  OriginTrialServiceFactory();
-  ~OriginTrialServiceFactory() override;
-
-  // BrowserContextKeyedServiceFactory:
-  std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
-      content::BrowserContext* context) const override;
-};
-
-}  // namespace tpcd::trial
-
-#endif  // CHROME_BROWSER_TPCD_SUPPORT_ORIGIN_TRIAL_SERVICE_FACTORY_H_
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 6863e30..4bd6251f 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -546,6 +546,7 @@
     "//components/safe_browsing/content/browser",
     "//components/safe_browsing/content/browser/password_protection",
     "//components/safe_browsing/content/browser/web_ui",
+    "//components/safe_browsing/core/browser:safe_browsing_metrics_collector",
     "//components/safe_browsing/core/browser/db:database_manager",
     "//components/safe_browsing/core/browser/db:util",
     "//components/safe_browsing/core/browser/password_protection:password_protection_metrics_util",
@@ -1430,6 +1431,7 @@
       "//chrome/browser/ui/passwords/bubble_controllers/password_change",
       "//chrome/browser/ui/performance_controls",
       "//chrome/browser/ui/performance_controls:impl",
+      "//chrome/browser/ui/promos:controller",
       "//chrome/browser/ui/search",
       "//chrome/browser/ui/search:impl",
       "//chrome/browser/ui/search_engine_choice",
@@ -1748,6 +1750,7 @@
       "//chrome/browser/ui/toolbar/pinned_toolbar:impl",
       "//chrome/browser/ui/bookmarks:impl",
       "//chrome/browser/ui/feature_first_run:impl",
+      "//chrome/browser/ui/promos:controller",
 
       # TODO(crbug.com/364667551): It includes quite a few files from //c/b/ui's
       # subdirectories such as views and webui which are not modularized yet.
@@ -2205,6 +2208,7 @@
       "//chrome/browser/ash/video_conference",
       "//chrome/browser/ash/wallpaper",
       "//chrome/browser/ash/wallpaper_handlers",
+      "//chrome/browser/certificate_provider",
       "//chrome/browser/chromeos/app_mode",
       "//chrome/browser/chromeos/arc",
       "//chrome/browser/chromeos/cros_apps",
diff --git a/chrome/browser/ui/android/device_lock/java/res/layout/device_lock_view.xml b/chrome/browser/ui/android/device_lock/java/res/layout/device_lock_view.xml
index d2a7def..02042b5f 100644
--- a/chrome/browser/ui/android/device_lock/java/res/layout/device_lock_view.xml
+++ b/chrome/browser/ui/android/device_lock/java/res/layout/device_lock_view.xml
@@ -81,32 +81,6 @@
                 android:text="@string/device_lock_notice"
                 android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
 
-            <FrameLayout
-                android:id="@+id/device_lock_notice_container"
-                android:layout_below="@id/device_lock_description"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_margin="20dp"
-                android:padding="16dp"
-                android:layout_gravity="center"
-                android:background="@drawable/rounded_corner_card">
-
-                <org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables
-                    android:id="@+id/device_lock_notice_legacy"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:gravity="start|center_vertical"
-                    android:drawableStart="@drawable/ic_error_outline_googblue_24dp"
-                    android:drawablePadding="10dp"
-                    android:ellipsize="end"
-                    android:text="@string/device_lock_notice"
-                    app:drawableTint="@color/default_icon_color_accent1_tint_list"
-                    app:drawableWidth="20dp"
-                    app:drawableHeight="20dp"
-                    style="@style/TextAppearance.TextMedium.Primary" />
-
-            </FrameLayout>
-
         </RelativeLayout>
 
     </ScrollView>
diff --git a/chrome/browser/ui/android/device_lock/java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockView.java b/chrome/browser/ui/android/device_lock/java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockView.java
index a8d0b4a..98403e7 100644
--- a/chrome/browser/ui/android/device_lock/java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockView.java
+++ b/chrome/browser/ui/android/device_lock/java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockView.java
@@ -19,9 +19,6 @@
 import org.chromium.components.browser_ui.widget.DualControlLayout;
 import org.chromium.components.browser_ui.widget.DualControlLayout.ButtonType;
 import org.chromium.components.browser_ui.widget.MaterialProgressBar;
-import org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables;
-import org.chromium.components.signin.SigninFeatureMap;
-import org.chromium.components.signin.SigninFeatures;
 
 /**
  * View that displays the device lock page to users and prompts them to create one if none are
@@ -33,7 +30,6 @@
     private TextView mTitle;
     private TextView mDescription;
     private TextView mNoticeText;
-    private TextViewWithCompoundDrawables mNoticeTextLegacy;
     private DualControlLayout mButtonBar;
     private Button mContinueButton;
     private Button mDismissButton;
@@ -57,23 +53,20 @@
         mProgressBar.setIndeterminate(true);
         mDescription = findViewById(R.id.device_lock_description);
         mNoticeText = findViewById(R.id.device_lock_notice);
-        mNoticeTextLegacy = findViewById(R.id.device_lock_notice_legacy);
 
-        int buttonWidth =
-                SigninFeatureMap.isEnabled(SigninFeatures.UNO_FOR_AUTO)
-                        ? ViewGroup.LayoutParams.MATCH_PARENT
-                        : ViewGroup.LayoutParams.WRAP_CONTENT;
         mDismissButton =
                 DualControlLayout.createButtonForLayout(
                         getContext(), ButtonType.SECONDARY_TEXT, "", null);
         mDismissButton.setLayoutParams(
-                new ViewGroup.LayoutParams(buttonWidth, ViewGroup.LayoutParams.WRAP_CONTENT));
+                new ViewGroup.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
 
         mContinueButton =
                 DualControlLayout.createButtonForLayout(
                         getContext(), ButtonType.PRIMARY_FILLED, "", null);
         mContinueButton.setLayoutParams(
-                new ViewGroup.LayoutParams(buttonWidth, ViewGroup.LayoutParams.WRAP_CONTENT));
+                new ViewGroup.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
 
         mButtonBar = findViewById(R.id.dual_control_button_bar);
         mButtonBar.addView(mContinueButton);
@@ -83,32 +76,21 @@
         MarginLayoutParams illustrationParams = (MarginLayoutParams) illustration.getLayoutParams();
         int illustrationTopMargin;
 
-        if (SigninFeatureMap.isEnabled(SigninFeatures.UNO_FOR_AUTO)) {
-            illustration.setBackgroundColor(Color.TRANSPARENT);
-            illustrationParams.height =
-                    getContext()
-                            .getResources()
-                            .getDimensionPixelSize(R.dimen.device_lock_dialog_illustration_height);
-            illustrationTopMargin =
-                    getContext()
-                            .getResources()
-                            .getDimensionPixelSize(
-                                    R.dimen.device_lock_dialog_illustration_top_margin);
-            findViewById(R.id.device_lock_notice_container).setVisibility(View.GONE);
-            mNoticeText.setVisibility(View.VISIBLE);
-            mButtonBar.setAlignment(DualControlLayout.DualControlLayoutAlignment.STACK);
-            DeviceLockUtils.updateDialogSubviewMargins(mTitle);
-            DeviceLockUtils.updateDialogSubviewMargins(mDescription);
-            DeviceLockUtils.updateDialogSubviewMargins(mNoticeText);
-            DeviceLockUtils.updateDialogSubviewMargins(mButtonBar);
-        } else {
-            illustration.setBackgroundColor(
-                    getContext().getColor(R.color.signin_header_animation_background));
-            illustrationTopMargin = 0;
-            findViewById(R.id.device_lock_notice_container).setVisibility(View.VISIBLE);
-            mNoticeText.setVisibility(View.GONE);
-            mButtonBar.setAlignment(DualControlLayout.DualControlLayoutAlignment.APART);
-        }
+        illustration.setBackgroundColor(Color.TRANSPARENT);
+        illustrationParams.height =
+                getContext()
+                        .getResources()
+                        .getDimensionPixelSize(R.dimen.device_lock_dialog_illustration_height);
+        illustrationTopMargin =
+                getContext()
+                        .getResources()
+                        .getDimensionPixelSize(R.dimen.device_lock_dialog_illustration_top_margin);
+        mNoticeText.setVisibility(View.VISIBLE);
+        mButtonBar.setAlignment(DualControlLayout.DualControlLayoutAlignment.STACK);
+        DeviceLockUtils.updateDialogSubviewMargins(mTitle);
+        DeviceLockUtils.updateDialogSubviewMargins(mDescription);
+        DeviceLockUtils.updateDialogSubviewMargins(mNoticeText);
+        DeviceLockUtils.updateDialogSubviewMargins(mButtonBar);
         illustrationParams.setMargins(
                 illustrationParams.leftMargin,
                 illustrationTopMargin,
@@ -130,9 +112,7 @@
     }
 
     TextView getNoticeText() {
-        return SigninFeatureMap.isEnabled(SigninFeatures.UNO_FOR_AUTO)
-                ? mNoticeText
-                : mNoticeTextLegacy;
+        return mNoticeText;
     }
 
     TextView getContinueButton() {
diff --git a/chrome/browser/ui/android/device_lock/java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockViewBinder.java b/chrome/browser/ui/android/device_lock/java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockViewBinder.java
index 355ea582..84d63b32 100644
--- a/chrome/browser/ui/android/device_lock/java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockViewBinder.java
+++ b/chrome/browser/ui/android/device_lock/java/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockViewBinder.java
@@ -6,15 +6,10 @@
 
 import android.view.View;
 
-import androidx.appcompat.content.res.AppCompatResources;
-
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.components.browser_ui.device_lock.DeviceLockActivityLauncher;
 import org.chromium.components.browser_ui.device_lock.DeviceLockDialogMetrics;
 import org.chromium.components.browser_ui.device_lock.DeviceLockDialogMetrics.DeviceLockDialogAction;
-import org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables;
-import org.chromium.components.signin.SigninFeatureMap;
-import org.chromium.components.signin.SigninFeatures;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -81,11 +76,7 @@
                             model.get(DeviceLockProperties.ON_USER_UNDERSTANDS_CLICKED));
             return;
         }
-        if (SigninFeatureMap.isEnabled(SigninFeatures.UNO_FOR_AUTO)) {
-            view.getContinueButton().setText(R.string.history_sync_primary_action);
-        } else {
-            view.getContinueButton().setText(R.string.device_lock_create_lock_button);
-        }
+        view.getContinueButton().setText(R.string.history_sync_primary_action);
         if (model.get(DeviceLockProperties.DEVICE_SUPPORTS_PIN_CREATION_INTENT)) {
             view.getContinueButton()
                     .setOnClickListener(
@@ -103,13 +94,6 @@
             view.getTitle().setTextAppearance(R.style.TextAppearance_Headline_Primary);
             view.getDescription().setTextAppearance(R.style.TextAppearance_TextMedium_Primary);
             view.getNoticeText().setTextAppearance(R.style.TextAppearance_TextMedium_Primary);
-            if (!SigninFeatureMap.isEnabled(SigninFeatures.UNO_FOR_AUTO)) {
-                ((TextViewWithCompoundDrawables) view.getNoticeText())
-                        .setDrawableTintColor(
-                                AppCompatResources.getColorStateList(
-                                        view.getContext(),
-                                        R.color.default_icon_color_accent1_tint_list));
-            }
             view.getContinueButton().setEnabled(true);
             view.getDismissButton().setEnabled(true);
         } else {
@@ -117,13 +101,6 @@
             view.getTitle().setTextAppearance(R.style.TextAppearance_Headline_Disabled);
             view.getDescription().setTextAppearance(R.style.TextAppearance_TextMedium_Disabled);
             view.getNoticeText().setTextAppearance(R.style.TextAppearance_TextMedium_Disabled);
-            if (!SigninFeatureMap.isEnabled(SigninFeatures.UNO_FOR_AUTO)) {
-                ((TextViewWithCompoundDrawables) view.getNoticeText())
-                        .setDrawableTintColor(
-                                AppCompatResources.getColorStateList(
-                                        view.getContext(),
-                                        R.color.default_text_color_disabled_list));
-            }
             view.getContinueButton().setEnabled(false);
             view.getDismissButton().setEnabled(false);
         }
@@ -137,11 +114,7 @@
                         .setOnClickListener(
                                 model.get(DeviceLockProperties.ON_USE_WITHOUT_AN_ACCOUNT_CLICKED));
             } else {
-                if (SigninFeatureMap.isEnabled(SigninFeatures.UNO_FOR_AUTO)) {
-                    view.getDismissButton().setText(R.string.history_sync_secondary_action);
-                } else {
-                    view.getDismissButton().setText(R.string.dialog_not_now);
-                }
+                view.getDismissButton().setText(R.string.history_sync_secondary_action);
                 view.getDismissButton()
                         .setOnClickListener(model.get(DeviceLockProperties.ON_DISMISS_CLICKED));
             }
diff --git a/chrome/browser/ui/android/device_lock/java/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockView.java b/chrome/browser/ui/android/device_lock/java/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockView.java
index 8d0b19ec..dc5bda4 100644
--- a/chrome/browser/ui/android/device_lock/java/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockView.java
+++ b/chrome/browser/ui/android/device_lock/java/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockView.java
@@ -17,8 +17,6 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.components.browser_ui.widget.DualControlLayout;
 import org.chromium.components.browser_ui.widget.DualControlLayout.ButtonType;
-import org.chromium.components.signin.SigninFeatureMap;
-import org.chromium.components.signin.SigninFeatures;
 
 /**
  * View shown to a user who has removed the device lock to inform them that their private data will
@@ -52,11 +50,6 @@
         mDescription = findViewById(R.id.missing_device_lock_description);
         mCheckBox = findViewById(R.id.missing_device_lock_remove_local_data);
 
-        int buttonWidth =
-                SigninFeatureMap.isEnabled(SigninFeatures.UNO_FOR_AUTO)
-                        ? ViewGroup.LayoutParams.MATCH_PARENT
-                        : ViewGroup.LayoutParams.WRAP_CONTENT;
-
         mCreateDeviceLockButton =
                 DualControlLayout.createButtonForLayout(
                         getContext(),
@@ -64,7 +57,8 @@
                         getResources().getString(R.string.device_lock_create_lock_button),
                         null);
         mCreateDeviceLockButton.setLayoutParams(
-                new ViewGroup.LayoutParams(buttonWidth, ViewGroup.LayoutParams.WRAP_CONTENT));
+                new ViewGroup.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
 
         mContinueButton =
                 DualControlLayout.createButtonForLayout(
@@ -73,7 +67,8 @@
                         getResources().getString(R.string.delete_and_continue),
                         null);
         mContinueButton.setLayoutParams(
-                new ViewGroup.LayoutParams(buttonWidth, ViewGroup.LayoutParams.WRAP_CONTENT));
+                new ViewGroup.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
 
         mButtonBar = findViewById(R.id.dual_control_button_bar);
         mButtonBar.addView(mContinueButton);
@@ -83,28 +78,20 @@
         MarginLayoutParams illustrationParams = (MarginLayoutParams) illustration.getLayoutParams();
         int illustrationTopMargin;
 
-        if (SigninFeatureMap.isEnabled(SigninFeatures.UNO_FOR_AUTO)) {
-            illustration.setBackgroundColor(Color.TRANSPARENT);
-            illustrationParams.height =
-                    getContext()
-                            .getResources()
-                            .getDimensionPixelSize(R.dimen.device_lock_dialog_illustration_height);
-            illustrationTopMargin =
-                    getContext()
-                            .getResources()
-                            .getDimensionPixelSize(
-                                    R.dimen.device_lock_dialog_illustration_top_margin);
-            mButtonBar.setAlignment(DualControlLayout.DualControlLayoutAlignment.STACK);
-            DeviceLockUtils.updateDialogSubviewMargins(mButtonBar);
-            DeviceLockUtils.updateDialogSubviewMargins(mTitle);
-            DeviceLockUtils.updateDialogSubviewMargins(mDescription);
-            DeviceLockUtils.updateDialogSubviewMargins(mCheckBox);
-        } else {
-            illustration.setBackgroundColor(
-                    getContext().getColor(R.color.signin_header_animation_background));
-            illustrationTopMargin = 0;
-            mButtonBar.setAlignment(DualControlLayout.DualControlLayoutAlignment.APART);
-        }
+        illustration.setBackgroundColor(Color.TRANSPARENT);
+        illustrationParams.height =
+                getContext()
+                        .getResources()
+                        .getDimensionPixelSize(R.dimen.device_lock_dialog_illustration_height);
+        illustrationTopMargin =
+                getContext()
+                        .getResources()
+                        .getDimensionPixelSize(R.dimen.device_lock_dialog_illustration_top_margin);
+        mButtonBar.setAlignment(DualControlLayout.DualControlLayoutAlignment.STACK);
+        DeviceLockUtils.updateDialogSubviewMargins(mButtonBar);
+        DeviceLockUtils.updateDialogSubviewMargins(mTitle);
+        DeviceLockUtils.updateDialogSubviewMargins(mDescription);
+        DeviceLockUtils.updateDialogSubviewMargins(mCheckBox);
         illustrationParams.setMargins(
                 illustrationParams.leftMargin,
                 illustrationTopMargin,
diff --git a/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockCoordinatorTest.java b/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockCoordinatorTest.java
index 8128b98a..e82a4e0 100644
--- a/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockCoordinatorTest.java
+++ b/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockCoordinatorTest.java
@@ -26,17 +26,14 @@
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.Batch;
-import org.chromium.base.test.util.Features;
 import org.chromium.chrome.browser.device_reauth.ReauthenticatorBridge;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.browser_ui.device_lock.DeviceLockActivityLauncher;
-import org.chromium.components.signin.SigninFeatures;
 import org.chromium.ui.test.util.BlankUiTestActivity;
 
 /** Tests for {@link DeviceLockCoordinator}. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @Batch(Batch.UNIT_TESTS)
-@Features.EnableFeatures(SigninFeatures.UNO_FOR_AUTO)
 public class DeviceLockCoordinatorTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockViewBinderTest.java b/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockViewBinderTest.java
index e70c01b..4513549 100644
--- a/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockViewBinderTest.java
+++ b/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/DeviceLockViewBinderTest.java
@@ -37,11 +37,9 @@
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.DisabledTest;
-import org.chromium.base.test.util.Features;
 import org.chromium.chrome.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.browser_ui.device_lock.DeviceLockActivityLauncher;
-import org.chromium.components.signin.SigninFeatures;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.test.util.BlankUiTestActivity;
@@ -51,7 +49,6 @@
 /** Tests for {@link DeviceLockViewBinder}. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @Batch(Batch.UNIT_TESTS)
-@Features.EnableFeatures(SigninFeatures.UNO_FOR_AUTO)
 public class DeviceLockViewBinderTest {
     @ClassRule
     public static BaseActivityTestRule<BlankUiTestActivity> sActivityTestRule =
diff --git a/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockCoordinatorTest.java b/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockCoordinatorTest.java
index de13a57..4269d0b 100644
--- a/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockCoordinatorTest.java
+++ b/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockCoordinatorTest.java
@@ -30,10 +30,8 @@
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.Batch;
-import org.chromium.base.test.util.Features;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.components.signin.SigninFeatures;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
 import org.chromium.ui.test.util.BlankUiTestActivity;
@@ -44,7 +42,6 @@
 /** Tests for {@link MissingDeviceLockCoordinator}. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @Batch(Batch.UNIT_TESTS)
-@Features.EnableFeatures(SigninFeatures.UNO_FOR_AUTO)
 public class MissingDeviceLockCoordinatorTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockLauncherTest.java b/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockLauncherTest.java
index f5f5a15..c0f474c 100644
--- a/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockLauncherTest.java
+++ b/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockLauncherTest.java
@@ -36,7 +36,6 @@
 import org.chromium.base.shared_preferences.SharedPreferencesManager;
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.util.Batch;
-import org.chromium.base.test.util.Features;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManagerFactory;
@@ -48,7 +47,6 @@
 import org.chromium.chrome.browser.signin.services.SigninManager;
 import org.chromium.chrome.browser.signin.services.SigninManager.DataWipeOption;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.components.signin.SigninFeatures;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.signin.identitymanager.IdentityManager;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
@@ -60,7 +58,6 @@
 /** Tests for the {@link MissingDeviceLockLauncher}. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @Batch(Batch.PER_CLASS)
-@Features.EnableFeatures(SigninFeatures.UNO_FOR_AUTO)
 public class MissingDeviceLockLauncherTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockViewBinderTest.java b/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockViewBinderTest.java
index bfcbdb4d..c32292bf 100644
--- a/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockViewBinderTest.java
+++ b/chrome/browser/ui/android/device_lock/javatests/src/org/chromium/chrome/browser/ui/device_lock/MissingDeviceLockViewBinderTest.java
@@ -28,9 +28,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.util.Batch;
-import org.chromium.base.test.util.Features;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.components.signin.SigninFeatures;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.test.util.BlankUiTestActivity;
@@ -40,7 +38,6 @@
 /** Tests for {@link MissingDeviceLockViewBinder}. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @Batch(Batch.UNIT_TESTS)
-@Features.EnableFeatures(SigninFeatures.UNO_FOR_AUTO)
 public class MissingDeviceLockViewBinderTest {
     @ClassRule
     public static BaseActivityTestRule<BlankUiTestActivity> sActivityTestRule =
diff --git a/chrome/browser/ui/android/extensions/windowing/test/java/src/org/chromium/chrome/browser/ui/extensions/windowing/ExtensionWindowControllerBridgeNativeUnitTestSupport.java b/chrome/browser/ui/android/extensions/windowing/test/java/src/org/chromium/chrome/browser/ui/extensions/windowing/ExtensionWindowControllerBridgeNativeUnitTestSupport.java
index e6b79630..7b85401 100644
--- a/chrome/browser/ui/android/extensions/windowing/test/java/src/org/chromium/chrome/browser/ui/extensions/windowing/ExtensionWindowControllerBridgeNativeUnitTestSupport.java
+++ b/chrome/browser/ui/android/extensions/windowing/test/java/src/org/chromium/chrome/browser/ui/extensions/windowing/ExtensionWindowControllerBridgeNativeUnitTestSupport.java
@@ -37,7 +37,9 @@
         // BrowserWindowInterface pointer that ExtensionWindowControllerBridge depends on.
         mChromeAndroidTask =
                 ChromeAndroidTaskUnitTestSupport.createChromeAndroidTaskWithMockDeps(
-                                FAKE_CHROME_ANDROID_TASK_ID, /* mockNatives= */ false)
+                                FAKE_CHROME_ANDROID_TASK_ID,
+                                /* mockNatives= */ false,
+                                /* isPendingTask= */ false)
                         .mChromeAndroidTask;
 
         mExtensionWindowControllerBridge =
diff --git a/chrome/browser/ui/android/multiwindow/java/res/layout/close_confirmation_dialog.xml b/chrome/browser/ui/android/multiwindow/java/res/layout/close_confirmation_dialog.xml
index 128de680..db8bb5c 100644
--- a/chrome/browser/ui/android/multiwindow/java/res/layout/close_confirmation_dialog.xml
+++ b/chrome/browser/ui/android/multiwindow/java/res/layout/close_confirmation_dialog.xml
@@ -41,6 +41,7 @@
             android:layout_marginStart="@dimen/dialog_padding_sides" >
             <CheckBox
                 android:id="@+id/no_more_check"
+                android:contentDescription="@string/dont_ask_again"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_marginBottom="12dp" />
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index 287be69a..26b358da 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -400,8 +400,6 @@
         } else {
             setTextDirection(TEXT_DIRECTION_LTR);
         }
-        // Always align to the same as the paragraph direction (LTR = left, RTL = right).
-        setTextAlignment(TEXT_ALIGNMENT_TEXT_START);
     }
 
     @Override
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 4f5cc50..389ef17 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -3535,9 +3535,6 @@
       <message name="IDS_DEVICE_LOCK_CREATE_LOCK_BUTTON" desc="Text for the button that navigates the user to create a profile lock on the device.">
         Create a car profile lock
       </message>
-      <message name="IDS_DIALOG_NOT_NOW" desc="Label for the negative ('not now') button of a dialog that prompts user action. Clicking on this button dismisses the dialog without further actions.">
-        Not now
-      </message>
 
       <!-- Strings for a missing device lock. -->
       <message name="IDS_MISSING_DEVICE_LOCK_TITLE" desc="Title shown on the missing profile lock page if a user has removed a profile lock that used to be present on the device.">
@@ -7105,7 +7102,7 @@
         Choose your address bar position
       </message>
       <message name="IDS_TIPS_PROMO_BOTTOM_SHEET_DESCRIPTION_BOTTOM_OMNIBOX" desc="Description of the Bottom Omnibox feature tip main page promo.">
-        Switch the position of your address bar to the top of bottom for a customaized browsing experience
+        Switch the position of your address bar to the top or bottom for a customized browsing experience
       </message>
     </messages>
   </release>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DIALOG_NOT_NOW.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DIALOG_NOT_NOW.png.sha1
deleted file mode 100644
index 4ecc378c..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_DIALOG_NOT_NOW.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-9f2152849862b49aa723e0a48b48867765ca9c00
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_TIPS_PROMO_BOTTOM_SHEET_DESCRIPTION_BOTTOM_OMNIBOX.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_TIPS_PROMO_BOTTOM_SHEET_DESCRIPTION_BOTTOM_OMNIBOX.png.sha1
index 0b9f3cc..210d045c 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_TIPS_PROMO_BOTTOM_SHEET_DESCRIPTION_BOTTOM_OMNIBOX.png.sha1
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_TIPS_PROMO_BOTTOM_SHEET_DESCRIPTION_BOTTOM_OMNIBOX.png.sha1
@@ -1 +1 @@
-7c9ddd8296643068d8c43444de81db1ed4f1864d
\ No newline at end of file
+d71944ddc88d85e1196c16409139712901f0b293
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_af.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_af.xtb
index 936824e..c485e54 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_af.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_af.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Leesmodus is altyd hier beskikbaar</translation>
 <translation id="331080746368555063">Privaat werwe kan dinge soos jou maatskappy se intranet insluit</translation>
 <translation id="3311330810461485557">Soek volgens app, datum en meer.</translation>
+<translation id="3331974543021145906">Programinligting</translation>
 <translation id="3334729583274622784">Verander lêeruitbreiding?</translation>
 <translation id="333863344734218290">Waarsku jou oor onveilige publieke en privaat werwe</translation>
 <translation id="3341262203274374114">Kan nie ontvolg nie. Iets was fout.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_am.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_am.xtb
index 0553b41..04f351f7 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_am.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_am.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">የንባብ ሁነታ ምንጊዜም እዚህ ይገኛል</translation>
 <translation id="331080746368555063">የግል ጣቢያዎች እንደ የድርጅትዎን ውስጠ መረብ ያሉ ነገሮችን ሊያካትቱ ይችላሉ</translation>
 <translation id="3311330810461485557">በመተግበሪያ፣ በቀን እና በሌሎች ይፈልጉ።</translation>
+<translation id="3331974543021145906">የመተግበሪያ መረጃ</translation>
 <translation id="3334729583274622784">የፋይል ቅጥያ ይቀየር?</translation>
 <translation id="333863344734218290">ደህንነታቸው ላልተጠበቀ ይፋዊ እና የግል ጣቢያዎች ያስጠነቅቅዎታል</translation>
 <translation id="3341262203274374114">መከተል አልተቻለም። የሆነ ችግር ተፈጥሯል።</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ar.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ar.xtb
index 5639e714..2d6755f 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ar.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ar.xtb
@@ -159,6 +159,7 @@
 <translation id="1726477445370128854">‏بإمكانك التصفّح والبحث بسرعة عندما يحمّل Chrome مُسبقًا الصفحات التي يتوقّع أن تنتقل إليها.</translation>
 <translation id="1728803206919861584">هل تريد حفظ مفتاح المرور خارج وضع التصفُّح المتخفي؟</translation>
 <translation id="1732514748744806741">إذا تم اختراق كلمات المرور الخاصّة بك، سنُعلمك بذلك</translation>
+<translation id="1737694586321018892">‏الحصول على أعلى مستوى من الأمان في متصفِّح Chrome</translation>
 <translation id="1747593111377567311">{NUM_SITES,plural, =1{أرسل موقع إلكتروني واحد الكثير من الإشعارات مؤخرًا}zero{أرسل # موقع إلكتروني الكثير من الإشعارات مؤخرًا}two{أرسل موقعان إلكترونيان الكثير من الإشعارات مؤخرًا}few{أرسلت # مواقع إلكترونية الكثير من الإشعارات مؤخرًا}many{أرسل # موقعًا إلكترونيًا الكثير من الإشعارات مؤخرًا}other{أرسل # موقع إلكتروني الكثير من الإشعارات مؤخرًا}}</translation>
 <translation id="1749561566933687563">مزامنة الإشارات المرجعية</translation>
 <translation id="1750238553597293878">‏مواصلة استخدام كلمات المرور في حسابك على Google</translation>
@@ -414,6 +415,7 @@
 <translation id="2909615210195135082">‏النظام الأساسي لإشعارات Google</translation>
 <translation id="2912345083818861431">استخدام قفل الشاشة للاطّلاع على علامات التبويب المفتوحة في وضع التصفُّح المتخفي</translation>
 <translation id="2918484639460781603">الانتقال إلى الإعدادات</translation>
+<translation id="2920776558188188922">يمكنك تبديل موضع شريط العناوين إلى أعلى الشاشة أو أسفلها للحصول على تجربة تصفُّح مخصَّصة</translation>
 <translation id="2923908459366352541">الاسم غير صحيح</translation>
 <translation id="2932150158123903946">‏مساحة تخزين <ph name="APP_NAME" /> Google</translation>
 <translation id="2932222164150889403">لن يتم تغيير اللغة في لوحة المفاتيح.</translation>
@@ -503,6 +505,7 @@
 <translation id="3309978850836730073">يتوفّر "وضع القراءة" دائمًا هنا</translation>
 <translation id="331080746368555063">قد تتضمّن المواقع الإلكترونية الخاصة محتوى مثل الشبكة الداخلية للشركة</translation>
 <translation id="3311330810461485557">البحث حسب التطبيق والتاريخ وغير ذلك</translation>
+<translation id="3331974543021145906">معلومات التطبيق</translation>
 <translation id="3334729583274622784">هل تريد تغيير امتداد الملف؟</translation>
 <translation id="333863344734218290">التحذير بشأن المواقع الإلكترونية العامة والخاصة غير الآمنة</translation>
 <translation id="3341262203274374114">يتعذّر إلغاء متابعة الخلاصة. حدث خطأ.</translation>
@@ -974,6 +977,7 @@
 <translation id="549660026907543296">الانضمام باسم "<ph name="MEMBER_FULL_NAME" />"</translation>
 <translation id="5517326820028369152">بطاقة الأنشطة الحديثة السفلية</translation>
 <translation id="5526281268548144413">لا يمكن إغلاق علامات التبويب في نوافذ متعددة</translation>
+<translation id="5532536403031755365">يمكنك إضافة طبقة حماية إضافية ضد التهديدات على الإنترنت باستخدام ميزة "الحماية المحسَّنة"</translation>
 <translation id="5548606607480005320">التحقّق من الأمان</translation>
 <translation id="5554520618550346933">‏عندما تستخدم كلمة مرور، يحذّرك Chrome مما إذا كان قد تم نشرها على الإنترنت. وأثناء هذه العملية، يتم تشفير كلمات المرور وأسماء المستخدمين حتى لا يتمكّن أي طرف آخر من الاطّلاع عليها، بما في ذلك Google.</translation>
 <translation id="5555525474779371165">اختيار حماية "التصفُّح الآمن"</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_as.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_as.xtb
index 7a079321..e2eeba5 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_as.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_as.xtb
@@ -159,6 +159,7 @@
 <translation id="1726477445370128854">আপুনি যাব পৰাৰ সম্ভাৱনা থকা পৃষ্ঠাসমূহ Chromeএ আগতীয়াকৈ ল’ড কৰিলে আপুনি খৰতকীয়াকৈ ব্ৰাউজ আৰু সন্ধান কৰিব পাৰে</translation>
 <translation id="1728803206919861584">ইনক’গনিট’ ম’ডৰ পৰা বাহিৰ হৈ পাছকী ছেভ কৰিবনে?</translation>
 <translation id="1732514748744806741">আপোনাৰ পাছৱৰ্ড হেক কৰা হ’লে আমি আপোনাক জনাম</translation>
+<translation id="1737694586321018892">Chromeৰ আটাইতকৈ শক্তিশালী সুৰক্ষা লাভ কৰক</translation>
 <translation id="1747593111377567311">{NUM_SITES,plural, =1{১ টা ছাইটে শেহতীয়াকৈ বহুতো জাননী পঠিয়াইছে}one{# টা ছাইটে শেহতীয়াকৈ বহুতো জাননী পঠিয়াইছে}other{# টা ছাইটে শেহতীয়াকৈ বহুতো জাননী পঠিয়াইছে}}</translation>
 <translation id="1749561566933687563">আপোনাৰ বুকমার্কসমূহ ছিংক কৰক</translation>
 <translation id="1750238553597293878">আপোনাৰ Google একাউণ্টত পাছৱৰ্ডসমূহ ব্যৱহাৰ কৰি থাকক</translation>
@@ -414,6 +415,7 @@
 <translation id="2909615210195135082">Googleৰ জাননীসমূহৰ প্লেটফৰ্ম</translation>
 <translation id="2912345083818861431">খোলা ইনক’গনিট’ টেব চাবলৈ স্ক্ৰীন লক ব্যৱহাৰ কৰক</translation>
 <translation id="2918484639460781603">ছেটিঙলৈ যাওক</translation>
+<translation id="2920776558188188922">এক কাষ্টমাইজ কৰা ব্ৰাউজিঙৰ অভিজ্ঞতা লাভ কৰিবলৈ আপোনাৰ ঠিকনাৰ বাৰডাল একেবাৰে ওপৰলৈ বা একেবাৰে তললৈ নিয়ক</translation>
 <translation id="2923908459366352541">নামটো অমান্য</translation>
 <translation id="2932150158123903946">Google <ph name="APP_NAME" /> ষ্ট'ৰেজ</translation>
 <translation id="2932222164150889403">আপোনাৰ কীব’ৰ্ড সলনি নহ’ব</translation>
@@ -502,6 +504,7 @@
 <translation id="3309978850836730073">ৰীডিং ম’ড সদায় ইয়াত উপলব্ধ হয়</translation>
 <translation id="331080746368555063">ব্যক্তিগত ছাইটসমূহে আপোনাৰ কোম্পানীৰ ইণ্ট্ৰানেটৰ দৰে বস্তুবোৰ অন্তৰ্ভুক্ত কৰিব পাৰে</translation>
 <translation id="3311330810461485557">এপ্‌, তাৰিখ আৰু অধিক অনুসৰি সন্ধান কৰক।</translation>
+<translation id="3331974543021145906">এপৰ তথ্য</translation>
 <translation id="3334729583274622784">ফাইলটোৰ এক্সটেনশ্বন সলনি কৰিবনে?</translation>
 <translation id="333863344734218290">আপোনাক অসুৰক্ষিত ৰাজহুৱা আৰু ব্যক্তিগত ছাইটৰ বাবে সকীয়নি দিয়ে</translation>
 <translation id="3341262203274374114">আনফ’ল’ কৰিব নোৱাৰি। কিবা ভুল হ’ল।</translation>
@@ -973,6 +976,7 @@
 <translation id="549660026907543296"><ph name="MEMBER_FULL_NAME" /> হিচাপে যোগদান কৰক</translation>
 <translation id="5517326820028369152">শেহতীয়া কাৰ্যকলাপৰ তলৰ শ্বীট</translation>
 <translation id="5526281268548144413">একাধিক ৱিণ্ড’ত বন্ধ কৰিব নোৱাৰি</translation>
+<translation id="5532536403031755365">বৰ্ধিত সুৰক্ষা ব্যৱহাৰ কৰি অনলাইন ভাবুকিৰ বিৰুদ্ধে সুৰক্ষাৰ এটা অতিৰিক্ত স্তৰ যোগ দিয়ক</translation>
 <translation id="5548606607480005320">সুৰক্ষা পৰীক্ষা</translation>
 <translation id="5554520618550346933">যেতিয়া আপুনি এটা পাছৱৰ্ড ব্যৱহাৰ কৰে, তেতিয়া সেইটো অনলাইনত প্ৰকাশ কৰা হৈছে নেকি সেই বিষয়ে Chromeএ আপোনাক সকীয়নি দিয়ে। এনে কৰোঁতে আপোনাৰ পাছৱৰ্ডসমূহ আৰু ব্যৱহাৰকাৰীৰ নামসমূহ এনক্ৰিপ্ট কৰা হয়, যাতে সেইবোৰ Googleকে ধৰি অন্য কোনেও পঢ়িব নোৱাৰে।</translation>
 <translation id="5555525474779371165">আপোনাৰ সুৰক্ষিত ব্ৰাউজিঙৰ সুৰক্ষা বাছনি কৰক</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_az.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_az.xtb
index 6a7599d..b6292df 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_az.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_az.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">Oxu rejimi həmişə burada əlçatandır</translation>
 <translation id="331080746368555063">Şəxsi saytlara şirkətinizin daxili şəbəkəsi kimi elementlər daxil ola bilər</translation>
 <translation id="3311330810461485557">Tətbiq, tarix və s. üzrə axtarın.</translation>
+<translation id="3331974543021145906">Tətbiq haqqında</translation>
 <translation id="3334729583274622784">Fayl artırması dəyişdirilsin?</translation>
 <translation id="333863344734218290">Güvənsiz ictimai və şəxsi saytlar barədə xəbərdarlıq edir</translation>
 <translation id="3341262203274374114">İzləmədən çıxarmaq olmur. Xəta baş verdi.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_be.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_be.xtb
index 936088b..dd933f1 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_be.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_be.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">Рэжым чытання можна ў любы час уключыць тут</translation>
 <translation id="331080746368555063">Да прыватных сайтаў можа адносіцца, напрыклад, інтранэт кампаніі</translation>
 <translation id="3311330810461485557">Пошук па праграме, даце і іншых даных.</translation>
+<translation id="3331974543021145906">Звесткі аб праграме</translation>
 <translation id="3334729583274622784">Змяніць пашырэнне файла?</translation>
 <translation id="333863344734218290">Папярэджваць пра небяспечныя агульнадаступныя і прыватныя сайты</translation>
 <translation id="3341262203274374114">Адпісацца не ўдалося. Нешта пайшло не так.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_bg.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_bg.xtb
index fb7724a..7a6d6ab 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_bg.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_bg.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Режимът на четене винаги е налице тук</translation>
 <translation id="331080746368555063">Частните сайтове може да включват различни неща, като например интранет на компанията ви</translation>
 <translation id="3311330810461485557">Търсете по приложение, дата и др.</translation>
+<translation id="3331974543021145906">Информация за приложението</translation>
 <translation id="3334729583274622784">Да се промени ли файловото разширение?</translation>
 <translation id="333863344734218290">Предупреждава ви за незащитени обществени и частни сайтове</translation>
 <translation id="3341262203274374114">Прекратяването на следенето не е възможно. Нещо се обърка.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_bn.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_bn.xtb
index 10123b1..7e46612 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_bn.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_bn.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">রিডিং মোড এখানে সবসময় উপলভ্য আছে</translation>
 <translation id="331080746368555063">ব্যক্তিগত সাইটে আপনার কোম্পানির ইন্ট্রানেটের মতো জিনিস থাকতে পারে</translation>
 <translation id="3311330810461485557">অ্যাপ, তারিখ ও আরও অনেক কিছু দিয়ে সার্চ করুন।</translation>
+<translation id="3331974543021145906">অ্যাপ্লিকেশানের তথ্য</translation>
 <translation id="3334729583274622784">ফাইলের এক্সটেনশন পরিবর্তন করতে চান?</translation>
 <translation id="333863344734218290">এর ফলে আপনি অসুরক্ষিত সাইট সম্পর্কে বিজ্ঞপ্তি পেতে পারেন</translation>
 <translation id="3341262203274374114">ফলো করা থামানো যাচ্ছে না। কোনও সমস্যা হয়েছে।</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_bs.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_bs.xtb
index 1b8b947..078e885 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_bs.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_bs.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Način rada za čitanje je uvijek dostupan ovdje</translation>
 <translation id="331080746368555063">Privatne web lokacije mogu uključivati stvari kao što je intranet vaše kompanije</translation>
 <translation id="3311330810461485557">Pretražujte prema aplikaciji, datumu i drugo.</translation>
+<translation id="3331974543021145906">Informacije o aplikaciji</translation>
 <translation id="3334729583274622784">Promijeniti ekstenziju fajla?</translation>
 <translation id="333863344734218290">Upozorava vas na nesigurne javne i privatne web lokacije</translation>
 <translation id="3341262203274374114">Nije moguće prestati pratiti. Nešto nije uredu.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ca.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ca.xtb
index e17e344..e4efdb6 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ca.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ca.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">El mode de lectura sempre està disponible aquí</translation>
 <translation id="331080746368555063">Els llocs web privats poden incloure elements com ara la intranet de la teva empresa</translation>
 <translation id="3311330810461485557">Cerca per aplicació, data i més.</translation>
+<translation id="3331974543021145906">Informació de l'aplicació</translation>
 <translation id="3334729583274622784">Vols canviar l'extensió del fitxer?</translation>
 <translation id="333863344734218290">T'avisa de llocs web públics i privats no segurs</translation>
 <translation id="3341262203274374114">No es pot deixar de seguir. S'ha produït un error.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_cs.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_cs.xtb
index b8400e20..0576456 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_cs.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_cs.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Režim čtení je kdykoli k dispozici tady</translation>
 <translation id="331080746368555063">Soukromé weby mohou zahrnovat věci, jako je intranet vaší společnosti</translation>
 <translation id="3311330810461485557">Vyhledávejte podle aplikace, data a dalších informací.</translation>
+<translation id="3331974543021145906">O aplikaci</translation>
 <translation id="3334729583274622784">Změnit příponu souboru?</translation>
 <translation id="333863344734218290">Varuje vás před nezabezpečenými veřejnými a soukromými weby</translation>
 <translation id="3341262203274374114">Sledování nelze zrušit. Něco se pokazilo.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_cy.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_cy.xtb
index c19ac64..4580bf4 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_cy.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_cy.xtb
@@ -503,6 +503,7 @@
 <translation id="3309978850836730073">Mae'r modd darllen bob amser ar gael yma</translation>
 <translation id="331080746368555063">Gall gwefannau preifat gynnwys pethau fel mewnrwyd eich cwmni</translation>
 <translation id="3311330810461485557">Chwilio yn ôl ap, dyddiad, a rhagor.</translation>
+<translation id="3331974543021145906">Gwybodaeth yr ap</translation>
 <translation id="3334729583274622784">Newid estyniad y ffeil?</translation>
 <translation id="333863344734218290">Yn eich rhybuddio am wefannau cyhoeddus a phreifat ansicr</translation>
 <translation id="3341262203274374114">Methu â dad-ddilyn. Aeth rhywbeth o'i le.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_da.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_da.xtb
index 3fe8a0f..66df3d23 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_da.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_da.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Læsetilstand er altid tilgængelig her</translation>
 <translation id="331080746368555063">Private websites kan f.eks. omfatte din virksomheds intranet</translation>
 <translation id="3311330810461485557">Søg efter app, dato med mere.</translation>
+<translation id="3331974543021145906">Appinfo</translation>
 <translation id="3334729583274622784">Vil du ændre filtypen?</translation>
 <translation id="333863344734218290">Advarer dig om usikre offentlige og private websites</translation>
 <translation id="3341262203274374114">"Følg ikke" kan ikke udføres. Der opstod en fejl.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_de.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_de.xtb
index fcdca0a..0ca9a85f 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_de.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_de.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Der Lesemodus ist immer hier verfügbar</translation>
 <translation id="331080746368555063">Eine private Website kann beispielsweise das Intranet deines Unternehmens sein</translation>
 <translation id="3311330810461485557">Du kannst nach App, Datum und mehr suchen.</translation>
+<translation id="3331974543021145906">App-Info</translation>
 <translation id="3334729583274622784">Dateiendung ändern?</translation>
 <translation id="333863344734218290">Warnt dich vor unsicheren öffentlichen und privaten Websites</translation>
 <translation id="3341262203274374114">Nicht mehr folgen nicht möglich. Ein Fehler ist aufgetreten.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_el.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_el.xtb
index 0b20b64..b5a0c67 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_el.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_el.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Η λειτουργία Ανάγνωσης είναι πάντα διαθέσιμη εδώ</translation>
 <translation id="331080746368555063">Οι ιδιωτικοί ιστότοποι μπορεί να περιλαμβάνουν διάφορα στοιχεία, όπως το εσωτερικό δίκτυο της εταιρείας σας</translation>
 <translation id="3311330810461485557">Αναζήτηση κατά εφαρμογή, ημερομηνία και άλλα.</translation>
+<translation id="3331974543021145906">Πληροφορίες εφαρμογής</translation>
 <translation id="3334729583274622784">Θέλετε να αλλάξετε την επέκταση αρχείου;</translation>
 <translation id="333863344734218290">Σας ειδοποιεί για μη ασφαλείς δημόσιους και ιδιωτικούς ιστοτόπους</translation>
 <translation id="3341262203274374114">Δεν είναι δυνατή η κατάργηση παρακολούθησης. Παρουσιάστηκε κάποιο πρόβλημα.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_en-GB.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_en-GB.xtb
index b773655..8eae74c1 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_en-GB.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_en-GB.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Reading mode is always available here</translation>
 <translation id="331080746368555063">Private sites might include things like your company's intranet</translation>
 <translation id="3311330810461485557">Search by app, date and more.</translation>
+<translation id="3331974543021145906">App info</translation>
 <translation id="3334729583274622784">Change file extension?</translation>
 <translation id="333863344734218290">Warns you for insecure public and private sites</translation>
 <translation id="3341262203274374114">Can’t unfollow. Something went wrong.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_es-419.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_es-419.xtb
index 5318c455..9c511cea 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_es-419.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_es-419.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">El Modo de lectura siempre está disponible aquí</translation>
 <translation id="331080746368555063">Los sitios privados pueden incluir, por ejemplo, la intranet de tu empresa</translation>
 <translation id="3311330810461485557">Busca por app, fecha y más.</translation>
+<translation id="3331974543021145906">Info. de la aplicación</translation>
 <translation id="3334729583274622784">¿Quieres cambiar la extensión del archivo?</translation>
 <translation id="333863344734218290">Te advierte sobre sitios públicos y privados no seguros</translation>
 <translation id="3341262203274374114">No se puede dejar de seguir. Se produjo un error.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_es.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_es.xtb
index 2cb03c6..2b8e6a6 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_es.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_es.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">El modo Lectura siempre está disponible aquí</translation>
 <translation id="331080746368555063">Puede que los sitios privados incluyan cosas como la intranet de tu empresa</translation>
 <translation id="3311330810461485557">Busca por aplicación, fecha y más.</translation>
+<translation id="3331974543021145906">Información de la app</translation>
 <translation id="3334729583274622784">¿Cambiar la extensión del archivo?</translation>
 <translation id="333863344734218290">Te avisa de los sitios públicos y privados no seguros</translation>
 <translation id="3341262203274374114">No se puede dejar de seguir. Se ha producido un error.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_et.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_et.xtb
index 9268be6b..919605d 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_et.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_et.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Lugemisrežiim on siin alati saadaval</translation>
 <translation id="331080746368555063">Privaatsed saidid võivad hõlmata näiteks teie ettevõtte intranetti.</translation>
 <translation id="3311330810461485557">Otsige rakenduse, kuupäeva ja muu järgi.</translation>
+<translation id="3331974543021145906">Rakenduse teave</translation>
 <translation id="3334729583274622784">Kas muuta faililaiendit?</translation>
 <translation id="333863344734218290">Hoiatab teid ebaturvaliste avalike ja privaatsete saitide eest</translation>
 <translation id="3341262203274374114">Ei saanud jälgimist lõpetada. Midagi läks valesti.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_eu.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_eu.xtb
index a2ef579..6359796 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_eu.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_eu.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Irakurtzeko modua hemen dago erabilgarri beti</translation>
 <translation id="331080746368555063">Webgune pribatuetan, zure enpresaren intraneta eta antzekoak sartzen dira, adibidez</translation>
 <translation id="3311330810461485557">Bilatu aplikazioaren, dataren eta beste irizpide batzuen arabera.</translation>
+<translation id="3331974543021145906">Aplikazioari buruzko informazioa</translation>
 <translation id="3334729583274622784">Fitxategi-luzapena aldatu nahi duzu?</translation>
 <translation id="333863344734218290">Webgune ez-seguru publiko eta pribatuei buruzko abisuak erakusten ditu</translation>
 <translation id="3341262203274374114">Ezin zaio jarraitzeari utzi. Arazoren bat izan da.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_fa.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_fa.xtb
index 9fa95303..330844c 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_fa.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_fa.xtb
@@ -159,6 +159,7 @@
 <translation id="1726477445370128854">‏وقتی Chrome صفحاتی را که فکر می‌کند ممکن است بازدید کنید پیش‌بارگیری می‌کند، می‌توانید سریع‌تر مرور و جستجو کنید.</translation>
 <translation id="1728803206919861584">گذرکلید خارج از «حالت ناشناس» ذخیره شود؟</translation>
 <translation id="1732514748744806741">اگر گذرواژه‌هایتان لو برود، به شما اطلاع خواهیم داد</translation>
+<translation id="1737694586321018892">‏دریافت بالاترین امنیت Chrome</translation>
 <translation id="1747593111377567311">{NUM_SITES,plural, =1{‫۱ سایت اخیراً تعداد زیادی اعلان ارسال کرده است}one{‫# سایت اخیراً تعداد زیادی اعلان ارسال کرده است}other{‫# سایت اخیراً تعداد زیادی اعلان ارسال کرده است}}</translation>
 <translation id="1749561566933687563">همگام‌سازی نشانک‌ها</translation>
 <translation id="1750238553597293878">‏همچنان از گذرواژه‌های ذخیره‌شده در «حساب Google» خود استفاده کنید</translation>
@@ -414,6 +415,7 @@
 <translation id="2909615210195135082">‏پلتفورم اعلان‌های Google</translation>
 <translation id="2912345083818861431">استفاده از قفل صفحه برای دیدن برگه‌های ناشناس باز</translation>
 <translation id="2918484639460781603">رفتن به تنظیمات</translation>
+<translation id="2920776558188188922">برای داشتن تجربه مرور سفارشی‌سازی‌شده، موقعیت نوار نشانی را به بالا یا پایین تغییر دهید</translation>
 <translation id="2923908459366352541">نام نامعتبر است</translation>
 <translation id="2932150158123903946">‏فضای ذخیره‌سازی Google <ph name="APP_NAME" /></translation>
 <translation id="2932222164150889403">صفحه‌کلیدتان تغییر نخواهد کرد</translation>
@@ -502,6 +504,7 @@
 <translation id="3309978850836730073">«حالت خواندن» همیشه اینجا دردسترس است</translation>
 <translation id="331080746368555063">سایت‌های خصوصی ممکن است شامل مواردی مثل اینترانت شرکت شما باشند</translation>
 <translation id="3311330810461485557">براساس برنامه، تاریخ، و موارد دیگر جستجو کنید.</translation>
+<translation id="3331974543021145906">اطلاعات برنامه</translation>
 <translation id="3334729583274622784">پسوند فایل تغییر کند؟</translation>
 <translation id="333863344734218290">درباره سایت‌های عمومی و خصوصی ناامن به شما هشدار می‌دهد</translation>
 <translation id="3341262203274374114">لغو دنبال کردن ممکن نیست. مشکلی پیش آمد.</translation>
@@ -973,6 +976,7 @@
 <translation id="549660026907543296">پیوستن با نام <ph name="MEMBER_FULL_NAME" /></translation>
 <translation id="5517326820028369152">برگ زیرین فعالیت اخیر</translation>
 <translation id="5526281268548144413">بستن در چند پنجره امکان‌پذیر نیست</translation>
+<translation id="5532536403031755365">با محافظت بهبودیافته، لایه محافظتی بیشتری دربرابر تهدیدهای آنلاین اضافه کنید</translation>
 <translation id="5548606607480005320">بررسی ایمنی</translation>
 <translation id="5554520618550346933">‏وقتی از گذرواژه‌ای استفاده کنید، Chrome به شما هشدار می‌دهد آیا این گذرواژه در فضای آنلاین منتشر شده است یا نه. هنگام انجام این کار، نام‌های کاربری و گذرواژه‌های شما رمزگذاری می‌شود تا اشخاص دیگر، ازجمله Google نتوانند آن‌ها را بخوانند.</translation>
 <translation id="5555525474779371165">انتخاب ویژگی حفاظتی «مرور ایمن»</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_fi.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_fi.xtb
index f5aa04e..b335675 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_fi.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_fi.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Lukutila on aina saatavilla täällä</translation>
 <translation id="331080746368555063">Yksityisiä sivustoja voivat olla esimerkiksi yrityksen intranet</translation>
 <translation id="3311330810461485557">Hae esimerkiksi sovelluksen tai päivämäärän perusteella.</translation>
+<translation id="3331974543021145906">Sovelluksen tiedot</translation>
 <translation id="3334729583274622784">Vaihdetaanko tiedostotunniste?</translation>
 <translation id="333863344734218290">Varoittaa suojaamattomista julkisista ja yksityisistä sivustoista</translation>
 <translation id="3341262203274374114">Ei voi seurata. Jotain meni pieleen.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_fil.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_fil.xtb
index abb50d7..293d7236 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_fil.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_fil.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">Palaging available ang Reading mode dito</translation>
 <translation id="331080746368555063">Puwedeng magsama ang mga pribadong site ng mga bagay tulad ng intranet ng iyong kumpanya</translation>
 <translation id="3311330810461485557">Maghanap ayon sa app, petsa, at higit pa.</translation>
+<translation id="3331974543021145906">Impormasyon ng app</translation>
 <translation id="3334729583274622784">Baguhin ang file extension?</translation>
 <translation id="333863344734218290">Binabalaan ka para sa mga hindi secure na pampubliko at pribadong site</translation>
 <translation id="3341262203274374114">Hindi ma-unfollow. Nagkaproblema.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_fr-CA.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_fr-CA.xtb
index b406e057..3763a86 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_fr-CA.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_fr-CA.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Le mode Lecture est toujours accessible ici</translation>
 <translation id="331080746368555063">Les sites privés peuvent inclure des éléments comme l'intranet de votre entreprise</translation>
 <translation id="3311330810461485557">Recherchez par appli, par date et plus encore.</translation>
+<translation id="3331974543021145906">Détails de l'application</translation>
 <translation id="3334729583274622784">Modifier l'extension du fichier?</translation>
 <translation id="333863344734218290">Vous avertit des sites publics et privés non sécurisés</translation>
 <translation id="3341262203274374114">Le désabonnement a échoué. Un problème est survenu.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_fr.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_fr.xtb
index 2f586ef9..3f41d4b 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_fr.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_fr.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Le mode Lecture est toujours disponible ici</translation>
 <translation id="331080746368555063">Les sites privés peuvent inclure l'intranet de votre entreprise, par exemple</translation>
 <translation id="3311330810461485557">Recherchez par appli, par date, etc.</translation>
+<translation id="3331974543021145906">Infos sur l'appli</translation>
 <translation id="3334729583274622784">Modifier l'extension du fichier ?</translation>
 <translation id="333863344734218290">Vous avertit pour les sites publics et privés non sécurisés</translation>
 <translation id="3341262203274374114">Impossible de vous désabonner en raison d'une erreur.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_gl.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_gl.xtb
index 35f0b3c..a6389f3 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_gl.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_gl.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">O modo de lectura sempre está dispoñible aquí</translation>
 <translation id="331080746368555063">Os sitios privados poden incluír, por exemplo, a intranet da túa empresa</translation>
 <translation id="3311330810461485557">Fai buscas por aplicación e data, entre outras características.</translation>
+<translation id="3331974543021145906">Información da aplicación</translation>
 <translation id="3334729583274622784">Queres cambiar a extensión do ficheiro?</translation>
 <translation id="333863344734218290">Recibir avisos dos sitios públicos e privados pouco seguros</translation>
 <translation id="3341262203274374114">Non se puido deixar de seguir o feed. Produciuse un erro.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_gu.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_gu.xtb
index d25c93e..48d7dcf 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_gu.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_gu.xtb
@@ -498,6 +498,7 @@
 <translation id="3309978850836730073">વાંચન મોડ હંમેશાં અહીં ઉપલબ્ધ છે</translation>
 <translation id="331080746368555063">ખાનગી સાઇટમાં તમારી કંપનીના ઇન્ટ્રાનેટ જેવી વસ્તુઓ શામેલ હોઈ શકે છે</translation>
 <translation id="3311330810461485557">ઍપ અને તારીખ જેવા બીજા ઘણા પરિમાણો મુજબ શોધો.</translation>
+<translation id="3331974543021145906">ઍપની માહિતી</translation>
 <translation id="3334729583274622784">ફાઇલનું એક્સ્ટેંશન બદલવું છે?</translation>
 <translation id="333863344734218290">અસુરક્ષિત સાર્વજનિક અને ખાનગી સાઇટ વિશે તમને ચેતવણી આપે છે</translation>
 <translation id="3341262203274374114">અનફૉલો કરી શકતા નથી. કંઈક ખોટું થયું.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_hi.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_hi.xtb
index df897ce..f989014d 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_hi.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_hi.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">यहां रीडिंग मोड हमेशा उपलब्ध रहता है</translation>
 <translation id="331080746368555063">निजी साइटों में, आपकी कंपनी के इंट्रानेट जैसी चीज़ें शामिल हो सकती हैं</translation>
 <translation id="3311330810461485557">ऐप्लिकेशन, तारीख वगैरह के हिसाब से खोजें.</translation>
+<translation id="3331974543021145906">ऐप्लिकेशन की जानकारी</translation>
 <translation id="3334729583274622784">फ़ाइल एक्सटेंशन बदलना चाहते हैं?</translation>
 <translation id="333863344734218290">इससे आपको सार्वजनिक और निजी साइटों के असुरक्षित होने की चेतावनी मिलती है</translation>
 <translation id="3341262203274374114">अनफ़ॉलो नहीं कर सकते. कोई गड़बड़ी हुई.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_hr.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_hr.xtb
index 1b483c7..ebf3469 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_hr.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_hr.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Čitački način uvijek je dostupan ovdje</translation>
 <translation id="331080746368555063">Privatne web-lokacije mogu uključivati stvari poput intraneta vaše tvrtke</translation>
 <translation id="3311330810461485557">Pretražujte po aplikaciji, datumu i drugom.</translation>
+<translation id="3331974543021145906">Informacije o aplikaciji</translation>
 <translation id="3334729583274622784">Promijeniti datotečni nastavak?</translation>
 <translation id="333863344734218290">Upozorava vas na nesigurne javne i privatne web-lokacije</translation>
 <translation id="3341262203274374114">Prestanak praćenja nije uspio. Došlo je do pogreške.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_hu.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_hu.xtb
index dac62db..d6ecde05 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_hu.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_hu.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">Az Olvasási mód mindig rendelkezésre áll itt</translation>
 <translation id="331080746368555063">A privát webhelyek közé tartozhat például a vállalat intranetje</translation>
 <translation id="3311330810461485557">Kereshet alkalmazás, dátum és egyebek szerint.</translation>
+<translation id="3331974543021145906">Alkalmazásadatok</translation>
 <translation id="3334729583274622784">Módosítja a fájl kiterjesztését?</translation>
 <translation id="333863344734218290">Figyelmeztetést kap a nyilvános és privát nem biztonságos webhelyekről</translation>
 <translation id="3341262203274374114">Nem sikerült felhagyni a követéssel. Hiba történt.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_hy.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_hy.xtb
index 18b80b88..fafc8de 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_hy.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_hy.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Ընթերցման ռեժիմը միշտ հասանելի է այստեղ</translation>
 <translation id="331080746368555063">Անձնական կայքերը պետք է ներառեն, օրինակ, ձեր ընկերության ինտրանետը</translation>
 <translation id="3311330810461485557">Որոնեք ըստ հավելվածի, ամսաթվի և այլ պարամետրերի։</translation>
+<translation id="3331974543021145906">Տեղեկություններ հավելվածի մասին</translation>
 <translation id="3334729583274622784">Փոխե՞լ ֆայլի ընդլայնումը</translation>
 <translation id="333863344734218290">Նախազգուշացնում է ձեզ ոչ ապահով հրապարակային և անձնական կայքերի մասին</translation>
 <translation id="3341262203274374114">Սխալի պատճառով չհաջողվեց չեղարկել բաժանորդագրությունը։</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_id.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_id.xtb
index 7bd77c5..54f8d9f 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_id.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_id.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Mode baca selalu tersedia di sini</translation>
 <translation id="331080746368555063">Situs pribadi mungkin mencakup hal-hal seperti intranet perusahaan Anda</translation>
 <translation id="3311330810461485557">Telusuri menurut aplikasi, tanggal, dan lainnya.</translation>
+<translation id="3331974543021145906">Info aplikasi</translation>
 <translation id="3334729583274622784">Ubah ekstensi file?</translation>
 <translation id="333863344734218290">Memperingatkan Anda tentang situs publik &amp; pribadi yang tidak aman</translation>
 <translation id="3341262203274374114">Tidak dapat berhenti mengikuti. Terjadi error.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_is.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_is.xtb
index 2494c97..0316538 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_is.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_is.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Lesstilling er alltaf aðgengileg hér</translation>
 <translation id="331080746368555063">Lokuð vefsvæði geta m.a. verið svæði á borð við innra net fyrirtækisins þíns</translation>
 <translation id="3311330810461485557">Leita eftir forriti, dagsetningu og fleiru.</translation>
+<translation id="3331974543021145906">Upplýsingar um forrit</translation>
 <translation id="3334729583274622784">Breyta skráarendingu?</translation>
 <translation id="333863344734218290">Varar þig við óöruggum opnum og lokuðum vefsvæðum</translation>
 <translation id="3341262203274374114">Ekki er hægt að hætta að fylgja. Eitthvað fór úrskeiðis.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_it.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_it.xtb
index 599ea5a..b171a68 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_it.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_it.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">La modalità Lettura è sempre disponibile qui</translation>
 <translation id="331080746368555063">I siti privati potrebbero includere elementi come l'intranet della tua azienda</translation>
 <translation id="3311330810461485557">Cerca per app, data e altro ancora.</translation>
+<translation id="3331974543021145906">Informazioni app</translation>
 <translation id="3334729583274622784">Vuoi cambiare l'estensione del file?</translation>
 <translation id="333863344734218290">Ti avvisa in caso di siti pubblici e privati non sicuri</translation>
 <translation id="3341262203274374114">Non è possibile non seguire più. Si è verificato un errore.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_iw.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_iw.xtb
index b7913d3..d4e14edf 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_iw.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_iw.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">התכונה "מצב קריאה" תמיד זמינה כאן</translation>
 <translation id="331080746368555063">אתרים פרטיים יכולים לכלול דברים כמו האינטראנט של החברה</translation>
 <translation id="3311330810461485557">אפשר לחפש לפי אפליקציה, תאריך ועוד.</translation>
+<translation id="3331974543021145906">פרטי האפליקציה</translation>
 <translation id="3334729583274622784">לשנות את סיומת הקובץ?</translation>
 <translation id="333863344734218290">קבלת אזהרה לגבי אתרים ציבוריים ופרטיים לא מאובטחים</translation>
 <translation id="3341262203274374114">לא ניתן לבטל את המעקב. משהו השתבש.</translation>
@@ -1833,6 +1834,7 @@
 <translation id="9133397713400217035">הצעות לתוכן במצב אופליין</translation>
 <translation id="9143389653531441385">הרשמה באמצעות <ph name="IDENTITY_PROVIDER_ETLD_PLUS_ONE" /></translation>
 <translation id="9148126808321036104">כניסה חוזרת</translation>
+<translation id="9148564897242852377">החלפת מצב הבחירה של הכרטיסייה</translation>
 <translation id="9157212632995922070">בחירת חשבון להמשך השימוש ב-<ph name="SITE_ETLD_PLUS_ONE" /></translation>
 <translation id="9158770349521403363">שיתוף התוכן בלבד</translation>
 <translation id="9161172953291434881">האם לדעתך הנתונים שלך יהיו בטוחים באתר הזה?</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ja.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ja.xtb
index f5fbd381..e1c034d 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ja.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ja.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">ここからいつでもリーディング モードを使用できます</translation>
 <translation id="331080746368555063">非公開サイトには、会社のイントラネットなどが含まれます</translation>
 <translation id="3311330810461485557">アプリや日付などで検索します。</translation>
+<translation id="3331974543021145906">アプリ情報</translation>
 <translation id="3334729583274622784">ファイル拡張子を変更しますか?</translation>
 <translation id="333863344734218290">安全でない公開サイトと非公開サイトについて警告する</translation>
 <translation id="3341262203274374114">フォローを解除できません。エラーが発生しました。</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ka.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ka.xtb
index 221d53792..e56f305 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ka.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ka.xtb
@@ -159,6 +159,7 @@
 <translation id="1726477445370128854">თუ Chrome წინასწარ ჩატვირთავს გვერდებს, რომლებიც, მისი გათვლებით, შეიძლება მოინახულოთ, ვების დათვალიერებისა და ძიების პროცესი დაჩქარდება</translation>
 <translation id="1728803206919861584">გსურთ, შეინახოთ წვდომის გასაღები ინკოგნიტო რეჟიმის მიღმა?</translation>
 <translation id="1732514748744806741">გაცნობებთ, თუ თქვენი პაროლები გატყდება</translation>
+<translation id="1737694586321018892">მიიღეთ Chrome-ის უძლიერესი დაცვა</translation>
 <translation id="1747593111377567311">{NUM_SITES,plural, =1{1-მა საიტმა ბევრი შეტყობინება გამოგიგზავნათ ბოლო დროს}other{#-მა საიტმა ბევრი შეტყობინება გამოგიგზავნათ ბოლო დროს}}</translation>
 <translation id="1749561566933687563">განახორციელეთ თქვენი სანიშნეების სინქრონიზაცია</translation>
 <translation id="1750238553597293878">გააგრძელეთ პაროლების გამოყენება თქვენს Google ანგარიშში</translation>
@@ -414,6 +415,7 @@
 <translation id="2909615210195135082">Google შეტყობინებების პლატფორმა</translation>
 <translation id="2912345083818861431">გახსნილი ინკოგნიტო ჩანართების სანახავად გამოიყენეთ ეკრანის დაბლოკვა</translation>
 <translation id="2918484639460781603">პარამეტრებზე გადასვლა</translation>
+<translation id="2920776558188188922">გადართეთ მისამართთა ზოლის პოზიცია ზემოთ ან ქვემოთ დათვალიერების მორგებული გამოცდილების მისაღებად</translation>
 <translation id="2923908459366352541">სახელი არასწორია</translation>
 <translation id="2932150158123903946">Google <ph name="APP_NAME" />-ის მეხსიერება</translation>
 <translation id="2932222164150889403">თქვენი კლავიატურა არ შეიცვლება</translation>
@@ -502,6 +504,7 @@
 <translation id="3309978850836730073">აქ წაკითხვის რეჟიმი ყოველთვის ხელმისაწვდომია</translation>
 <translation id="331080746368555063">პირადი საიტები შეიძლება შეიცავდეს თქვენი კომპანიის ინტრანეტის მსგავს სისტემებს</translation>
 <translation id="3311330810461485557">აპით, თარიღით და სხვა პარამეტრებით ძიება.</translation>
+<translation id="3331974543021145906">პროგრამის ინფორმაცია</translation>
 <translation id="3334729583274622784">გსურთ ფაილის გაფართოების შეცვლა?</translation>
 <translation id="333863344734218290">გაგაფრთხილებთ საჯარო და პირადი დაუცველი საიტების შესახებ</translation>
 <translation id="3341262203274374114">თვალის მიდევნების გაუქმება ვერ მოხერხდა. წარმოიქმნა შეფერხება.</translation>
@@ -973,6 +976,7 @@
 <translation id="549660026907543296">შეერთება სახელით <ph name="MEMBER_FULL_NAME" /></translation>
 <translation id="5517326820028369152">ბოლო აქტივობების ქვედა ფურცელი</translation>
 <translation id="5526281268548144413">სხვადასხვა ფანჯრებში დახურვა შეუძლებელია</translation>
+<translation id="5532536403031755365">გაძლიერებული დაცვის მეშვეობით დაამატეთ უსაფრთხოების დამატებითი შრე ონლაინ საფრთხეების წინააღმდეგ</translation>
 <translation id="5548606607480005320">უსაფრთხოების შემოწმება</translation>
 <translation id="5554520618550346933">პაროლის გამოყენებისას Chrome გაგაფრთხილებთ ხოლმე, თუ აღმოჩნდება, რომ ის ონლაინ გამოქვეყნდა. აღნიშნული ქმედებისას თქვენი პაროლები და მომხმარებლის სახელები დაშიფრული იქნება, რაც ნიშნავს, რომ მათ ვერავინ (მათ შორის, ვერც Google) წაიკითხავს.</translation>
 <translation id="5555525474779371165">აირჩიეთ სასურველი Safe Browsing-ის დაცვის დონე</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_kk.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_kk.xtb
index dba977f..fa15153f 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_kk.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_kk.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Оқу режимі үнемі осы жерде болады</translation>
 <translation id="331080746368555063">Жеке сайттарға компанияңыздың интранет желісі сияқты нәрселер кіруі мүмкін</translation>
 <translation id="3311330810461485557">Қолданба, күн және т.б. бойынша іздеңіз.</translation>
+<translation id="3331974543021145906">Қолданба ақпараты</translation>
 <translation id="3334729583274622784">Файл кеңейтімін өзгертесіз бе?</translation>
 <translation id="333863344734218290">Баршаға ашық қауіпті және жеке сайттар туралы ескертеді</translation>
 <translation id="3341262203274374114">Жазылудан бас тарту мүмкін емес. Бірдеңе дұрыс болмады.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_km.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_km.xtb
index 4aeae2b..72aedaf 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_km.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_km.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">អាចប្រើ​មុខងារអាន​នៅទីនេះ​បានជានិច្ច</translation>
 <translation id="331080746368555063">គេហទំព័រឯកជនអាចរួមបញ្ចូលអ្វីៗ ដូចជាអ៊ីនត្រាណិតរបស់ក្រុមហ៊ុនអ្នកជាដើម</translation>
 <translation id="3311330810461485557">ស្វែងរកតាមកម្មវិធី កាលបរិច្ឆេទ និងអ្វីៗជាច្រើនទៀត។</translation>
+<translation id="3331974543021145906">ព័ត៌មានកម្មវិធី</translation>
 <translation id="3334729583274622784">ប្ដូរកន្ទុយ​ឯកសារឬ?</translation>
 <translation id="333863344734218290">ប្រាប់​ឱ្យអ្នក​ប្រុងប្រយ័ត្ន​ចំពោះ​គេហទំព័រ​សាធារណៈ និងឯកជនដែលគ្មាន​សុវត្ថិភាព</translation>
 <translation id="3341262203274374114">មិនអាចឈប់​តាមដានបានទេ។ មានអ្វីមួយខុសប្រក្រតី។</translation>
@@ -1833,6 +1834,7 @@
 <translation id="9133397713400217035">រុករក​ដោយគ្មានអ៊ីនធឺណិត</translation>
 <translation id="9143389653531441385">ចុះឈ្មោះ​តាមរយៈ <ph name="IDENTITY_PROVIDER_ETLD_PLUS_ONE" /></translation>
 <translation id="9148126808321036104">ចូលម្តងទៀត</translation>
+<translation id="9148564897242852377">បិទ/បើក​ការជ្រើសរើស​ផ្ទាំង</translation>
 <translation id="9157212632995922070">ជ្រើសរើស​គណនី ដើម្បីបន្ត​នៅលើ <ph name="SITE_ETLD_PLUS_ONE" /></translation>
 <translation id="9158770349521403363">ចែករំលែក​ខ្លឹមសារ​តែប៉ុណ្ណោះ</translation>
 <translation id="9161172953291434881">តើអ្នក​ទុកចិត្តឱ្យ​គេហទំព័រនេះ​ប្រើប្រាស់​ទិន្នន័យ​របស់អ្នក​ដែរទេ?</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_kn.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_kn.xtb
index 1a37a36..1789ccc 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_kn.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_kn.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">ರೀಡಿಂಗ್ ಮೋಡ್ ಯಾವಾಗಲೂ ಇಲ್ಲಿ ಲಭ್ಯವಿದೆ</translation>
 <translation id="331080746368555063">ಖಾಸಗಿ ಸೈಟ್‌ಗಳು ನಿಮ್ಮ ಕಂಪನಿಯ ಇಂಟ್ರಾನೆಟ್‌ನಂತಹ ವಿಷಯಗಳನ್ನು ಒಳಗೊಂಡಿರಬಹುದು</translation>
 <translation id="3311330810461485557">ಆ್ಯಪ್, ದಿನಾಂಕ ಮತ್ತು ಹೆಚ್ಚಿನವುಗಳ ಪ್ರಕಾರ ಹುಡುಕಿ.</translation>
+<translation id="3331974543021145906">ಅಪ್ಲಿಕೇಶನ್‌ ಮಾಹಿತಿ</translation>
 <translation id="3334729583274622784">ಫೈಲ್ ವಿಸ್ತರಣೆಯನ್ನು ಬದಲಿಸುವುದೇ?</translation>
 <translation id="333863344734218290">ಅಸುರಕ್ಷಿತವಾದ ಸಾರ್ವಜನಿಕ ಮತ್ತು ಖಾಸಗಿ ಸೈಟ್‌ಗಳ ಕುರಿತು ನಿಮ್ಮನ್ನು ಎಚ್ಚರಿಸುತ್ತದೆ</translation>
 <translation id="3341262203274374114">ಅನುಸರಿಸುವುದನ್ನು ರದ್ದುಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಏನೋ ತಪ್ಪಾಗಿದೆ.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ko.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ko.xtb
index f435cf5..c412070 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ko.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ko.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">여기에서 항상 읽기 모드를 사용할 수 있습니다.</translation>
 <translation id="331080746368555063">비공개 사이트에는 회사 인트라넷 등이 포함될 수 있습니다</translation>
 <translation id="3311330810461485557">앱, 날짜 등으로 검색하세요.</translation>
+<translation id="3331974543021145906">앱 정보</translation>
 <translation id="3334729583274622784">파일 확장자를 변경하시겠습니까?</translation>
 <translation id="333863344734218290">안전하지 않은 공개 및 비공개 사이트에 대해 경고 표시</translation>
 <translation id="3341262203274374114">팔로우를 해제할 수 없습니다. 문제가 발생했습니다.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ky.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ky.xtb
index f6a17698..f9512d70 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ky.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ky.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Окуу режими бул жерде ар дайым жеткиликтүү</translation>
 <translation id="331080746368555063">Жеке сайттар компанияңыздын интранети сыяктуу нерселерди камтышы мүмкүн</translation>
 <translation id="3311330810461485557">Колдонмо, күн жана башкалар боюнча издөө.</translation>
+<translation id="3331974543021145906">Колдонмо маалыматы</translation>
 <translation id="3334729583274622784">Файлдын кеңейтүүсү өчүрүлсүнбү?</translation>
 <translation id="333863344734218290">Кооптуу коомдук жана жеке сайттар үчүн эскертет</translation>
 <translation id="3341262203274374114">Жазылуу токтотулган жок. Бир жерден ката кетти.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_lo.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_lo.xtb
index 8129674b..fc4de3c5 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_lo.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_lo.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">ໂໝດການອ່ານພ້ອມໃຫ້ນຳໃຊ້ຢູ່ບ່ອນນີ້ສະເໝີ</translation>
 <translation id="331080746368555063">ເວັບໄຊສ່ວນຕົວອາດຮວມມີສິ່ງຕ່າງໆ ເຊັ່ນ: ອິນທຣາເນັດຂອງບໍລິສັດຂອງທ່ານ</translation>
 <translation id="3311330810461485557">ຊອກຫາຕາມແອັບ, ວັນທີ ແລະ ອື່ນໆ.</translation>
+<translation id="3331974543021145906">ຂໍ້​ມູນ​ແອັບ</translation>
 <translation id="3334729583274622784">ປ່ຽນນາມສະກຸນໄຟລ໌ບໍ?</translation>
 <translation id="333863344734218290">ເຕືອນທ່ານຫາກພົບເວັບໄຊສາທາລະນະ ແລະ ເວັບໄຊສ່ວນຕົວທີ່ບໍ່ປອດໄພ</translation>
 <translation id="3341262203274374114">ບໍ່ສາມາດເຊົາຕິດຕາມໄດ້. ມີບາງຢ່າງຜິດພາດເກີດຂຶ້ນ.</translation>
@@ -1833,6 +1834,7 @@
 <translation id="9133397713400217035">ສຳຫຼວດອອບລາຍ</translation>
 <translation id="9143389653531441385">ລົງທະບຽນດ້ວຍ <ph name="IDENTITY_PROVIDER_ETLD_PLUS_ONE" /></translation>
 <translation id="9148126808321036104">ລົງຊື່ເຂົ້າ​ໃຊ້ອີກ</translation>
+<translation id="9148564897242852377">ສະຫຼັບການເລືອກແຖບ</translation>
 <translation id="9157212632995922070">ເລືອກບັນຊີເພື່ອດຳເນີນການຕໍ່ຢູ່ <ph name="SITE_ETLD_PLUS_ONE" /></translation>
 <translation id="9158770349521403363">ແບ່ງປັນເນື້ອຫາເທົ່ານັ້ນ</translation>
 <translation id="9161172953291434881">ທ່ານໄວ້ໃຈໃຫ້ເວັບໄຊນີ້ເຂົ້າເຖິງຂໍ້ມູນຂອງທ່ານບໍ?</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_lt.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_lt.xtb
index 2b03d867..f0566ad 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_lt.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_lt.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">Skaitymo režimas visada pasiekiamas čia</translation>
 <translation id="331080746368555063">Privačios svetainės gali apimti tokius dalykus kaip įmonės intranetas</translation>
 <translation id="3311330810461485557">Ieškoti pagal programą, datą ir kt.</translation>
+<translation id="3331974543021145906">Programos informacija</translation>
 <translation id="3334729583274622784">Pakeisti failo plėtinį?</translation>
 <translation id="333863344734218290">Įspėja apie nesaugias viešas ir privačias svetaines</translation>
 <translation id="3341262203274374114">Negalima nebestebėti. Kažkas ne taip.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_lv.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_lv.xtb
index 5bf243db..77c6e7104 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_lv.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_lv.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Lasīšanas režīms vienmēr ir pieejams šeit.</translation>
 <translation id="331080746368555063">Privātas vietnes var būt, piemēram, jūsu uzņēmuma iekštīkls.</translation>
 <translation id="3311330810461485557">Meklējiet pēc lietotnes, datuma un citiem parametriem.</translation>
+<translation id="3331974543021145906">Informācija par lietotni</translation>
 <translation id="3334729583274622784">Vai mainīt faila paplašinājumu?</translation>
 <translation id="333863344734218290">Tiek rādīti brīdinājumi par nedrošām publiskām un privātām vietnēm</translation>
 <translation id="3341262203274374114">Nevar pārtraukt sekošanu. Radās kļūda.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_mk.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_mk.xtb
index 7bbd3a3..878fa99 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_mk.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_mk.xtb
@@ -498,6 +498,7 @@
 <translation id="3309978850836730073">„Режимот за читање“ е секогаш достапен овде</translation>
 <translation id="331080746368555063">Приватните сајтови може да опфаќаат работи како интранетот на вашата компанија</translation>
 <translation id="3311330810461485557">Пребарувајте според апликација, датум и друго.</translation>
+<translation id="3331974543021145906">Информации за апликациите</translation>
 <translation id="3334729583274622784">Дали да се промени наставката на датотеката?</translation>
 <translation id="333863344734218290">Ве предупредува за небезбедни јавни и приватни сајтови</translation>
 <translation id="3341262203274374114">Не може да се отследи. Нешто тргна наопаку.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ml.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ml.xtb
index b636ed59..ff12001 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ml.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ml.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">റീഡിംഗ് മോഡ് എല്ലായ്‌പ്പോഴും ഇവിടെ ലഭ്യമാണ്</translation>
 <translation id="331080746368555063">നിങ്ങളുടെ കമ്പനിയുടെ ഇൻട്രാനെറ്റ് പോലുള്ള കാര്യങ്ങൾ സ്വകാര്യ സൈറ്റുകളിൽ ഉൾപ്പെട്ടേക്കാം</translation>
 <translation id="3311330810461485557">ആപ്പ്, തീയതി എന്നിവയും മറ്റും അനുസരിച്ച് തിരയുക.</translation>
+<translation id="3331974543021145906">ആപ്പ് വിവരം</translation>
 <translation id="3334729583274622784">ഫയൽ എക്സ്റ്റൻഷൻ മാറ്റണോ?</translation>
 <translation id="333863344734218290">സുരക്ഷിതമല്ലാത്ത പൊതു, സ്വകാര്യ സൈറ്റുകൾ സംബന്ധിച്ച് നിങ്ങൾക്ക് മുന്നറിയിപ്പ് നൽകുന്നു</translation>
 <translation id="3341262203274374114">പിന്തുടരുന്നത് ഒഴിവാക്കാനാകുന്നില്ല. എന്തോ കുഴപ്പമുണ്ടായി.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_mn.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_mn.xtb
index 9773176d..11bdef8 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_mn.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_mn.xtb
@@ -159,6 +159,7 @@
 <translation id="1726477445370128854">Chrome таныг зочилж магадгүй гэж бодсон хуудаснуудыг урьдчилан ачаалсан үед та илүү хурдан үзэж, хайх боломжтой</translation>
 <translation id="1728803206919861584">Нууцлалтай горимоос гадуур нэвтрэх түлхүүрийг хадгалах уу?</translation>
 <translation id="1732514748744806741">Бид таны нууц үг алдагдсан тохиолдолд танд мэдэгдэнэ</translation>
+<translation id="1737694586321018892">Chrome-н хамгийн хүчирхэг аюулгүй байдлыг аваарай</translation>
 <translation id="1747593111377567311">{NUM_SITES,plural, =1{1 сайт саяхан олон мэдэгдэл илгээсэн}other{# сайт саяхан олон мэдэгдэл илгээсэн}}</translation>
 <translation id="1749561566933687563">Хавчуургаа синхрончлоорой</translation>
 <translation id="1750238553597293878">Google Бүртгэлдээ нууц үгнүүдийг үргэлжлүүлэн ашиглана уу</translation>
@@ -414,6 +415,7 @@
 <translation id="2909615210195135082">Google мэдэгдлийн платформ</translation>
 <translation id="2912345083818861431">Нээлттэй Нууцлалтай табуудыг харахын тулд дэлгэцийн түгжээг ашиглана уу</translation>
 <translation id="2918484639460781603">Тохиргоо руу очих</translation>
+<translation id="2920776558188188922">Тусгайлан өөрчилсөн үзэх хэрэглээнд зориулж хаяг оруулах хэсгийнхээ байрлалыг дээш, доош сэлгээрэй</translation>
 <translation id="2923908459366352541">Нэр буруу байна</translation>
 <translation id="2932150158123903946">Google <ph name="APP_NAME" /> сан</translation>
 <translation id="2932222164150889403">Таны гар өөрчлөгдөхгүй</translation>
@@ -502,6 +504,7 @@
 <translation id="3309978850836730073">Унших горимыг үргэлж эндээс ашиглаж болно</translation>
 <translation id="331080746368555063">Хувийн сайтууд танай компанийн дотоод сүлжээ зэрэг зүйлсийг багтааж магадгүй</translation>
 <translation id="3311330810461485557">Апп, огноо болон бусад зүйлээр хайгаарай.</translation>
+<translation id="3331974543021145906">Аппликейшний мэдээлэл...</translation>
 <translation id="3334729583274622784">Файлын өргөтгөлийг өөрчлөх үү?</translation>
 <translation id="333863344734218290">Танд олон нийтийн болон хувийн аюултай сайтуудын талаар сануулна</translation>
 <translation id="3341262203274374114">Дагахаа болих боломжгүй. Алдаа гарлаа.</translation>
@@ -973,6 +976,7 @@
 <translation id="549660026907543296"><ph name="MEMBER_FULL_NAME" />-р нэгдэх</translation>
 <translation id="5517326820028369152">Саяхны үйл ажиллагааны доод хүснэгт</translation>
 <translation id="5526281268548144413">Олон цонхон дээр хаалттай байж болохгүй</translation>
+<translation id="5532536403031755365">Сайжруулсан хамгаалалтаар онлайн заналхийллийн эсрэг хамгаалалтын нэмэлт давхарга нэмээрэй</translation>
 <translation id="5548606607480005320">Аюулгүй байдлын шалгалт</translation>
 <translation id="5554520618550346933">Таныг нууц үг ашиглах үед үүнийг онлайнд нийтэлсэн тохиолдолд Chrome танд сануулна. Үүнийг хийх үед таны нууц үг болон хэрэглэгчийн нэрийг шифрлэх бөгөөд ингэснээр Google-г оруулаад хэн ч тэдгээрийг унших боломжгүй.</translation>
 <translation id="5555525474779371165">Аюулгүй үзэх хамгаалалтаа сонгоно уу</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_mr.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_mr.xtb
index 87603b0..af871858 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_mr.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_mr.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">इथे वाचन मोड नेहमीच उपलब्ध असतो</translation>
 <translation id="331080746368555063">खाजगी साइटवर तुमच्या कंपनीच्या इंट्रानेटसारख्या गोष्टींचा समावेश असू शकतो</translation>
 <translation id="3311330810461485557">ॲप, तारीख आणि आणखी बऱ्याच गोष्टींद्वारे शोधा.</translation>
+<translation id="3331974543021145906">ॲप माहिती</translation>
 <translation id="3334729583274622784">फाइल एक्स्टेंशन बदलायचे आहे का?</translation>
 <translation id="333863344734218290">तुम्हाला असुरक्षित सार्वजनिक आणि खाजगी साइटसाठी चेतावणी देते</translation>
 <translation id="3341262203274374114">अनफॉलो करू शकत नाही. काहीतरी चूक झाली.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ms.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ms.xtb
index 7275b712..3caf7c7 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ms.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ms.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">Mod membaca sentiasa tersedia di sini</translation>
 <translation id="331080746368555063">Laman peribadi mungkin merangkumi perkara seperti intranet syarikat anda</translation>
 <translation id="3311330810461485557">Cari mengikut apl, tarikh dan pelbagai lagi.</translation>
+<translation id="3331974543021145906">Maklumat apl</translation>
 <translation id="3334729583274622784">Tukar sambungan fail?</translation>
 <translation id="333863344734218290">Memberi anda amaran untuk laman awam &amp; peribadi yang tidak selamat</translation>
 <translation id="3341262203274374114">Tidak dapat berhenti mengikuti. Kesilapan telah berlaku.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_my.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_my.xtb
index 2ee1783f8..42442fb95 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_my.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_my.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">စာဖတ်မုဒ်ကို ဤနေရာတွင် အမြဲရနိုင်သည်</translation>
 <translation id="331080746368555063">သီးသန့်ဝဘ်ဆိုက်များတွင် သင့်ကုမ္ပဏီ၏ အင်တရာနက်ကဲ့သို့သော အရာများ ပါဝင်နိုင်သည်</translation>
 <translation id="3311330810461485557">အက်ပ်၊ ရက်စွဲ စသည်ဖြင့် ရှာပါ။</translation>
+<translation id="3331974543021145906">အက်ပ်အင်ဖို</translation>
 <translation id="3334729583274622784">ဖိုင်နောက်ဆက်တွဲ ပြောင်းမလား။</translation>
 <translation id="333863344734218290">မလုံခြုံသော အများသုံးနှင့် သီးသန့် ဝဘ်ဆိုက်များအကြောင်း သတိပေးရန်</translation>
 <translation id="3341262203274374114">လိုက်မကြည့်တော့၍ မရပါ။ တစ်ခုခုမှားသွားသည်။</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ne.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ne.xtb
index c22940f..87f472a5 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ne.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ne.xtb
@@ -498,6 +498,7 @@
 <translation id="3309978850836730073">रिडिङ मोड सधैँ यहाँ उपलब्ध छ</translation>
 <translation id="331080746368555063">निजी साइटहरूमा तपाईंको कम्पनीको इन्ट्रानेटलगायतका कुराहरू पर्न सक्छन्</translation>
 <translation id="3311330810461485557">एप, मिति र अन्य कुराअनुसार खोज्नुहोस्।</translation>
+<translation id="3331974543021145906">एप जानकारी...</translation>
 <translation id="3334729583274622784">फाइलको एक्स्टेन्सन परिवर्तन गर्ने हो?</translation>
 <translation id="333863344734218290">तपाईंलाई असुरक्षित सार्वजनिक तथा निजी साइटहरूका बारेमा चेतावनी दिन्छ</translation>
 <translation id="3341262203274374114">अनफलो गर्न सकिएन। कुनै समस्या आयो।</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_nl.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_nl.xtb
index c19c7cc..865a7fcf 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_nl.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_nl.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">De leesmodus is hier altijd beschikbaar</translation>
 <translation id="331080746368555063">Privésites kunnen bijvoorbeeld het intranet van je bedrijf zijn</translation>
 <translation id="3311330810461485557">Zoek bijvoorbeeld op app of datum.</translation>
+<translation id="3331974543021145906">App-informatie</translation>
 <translation id="3334729583274622784">Bestandsextensie wijzigen?</translation>
 <translation id="333863344734218290">Waarschuwt je voor niet-beveiligde openbare en privésites</translation>
 <translation id="3341262203274374114">Niet meer volgen is mislukt. Er is iets misgegaan.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_no.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_no.xtb
index 7269927..7cc0e2c4 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_no.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_no.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Lesemodus er alltid tilgjengelig her</translation>
 <translation id="331080746368555063">Private nettsteder kan for eksempel være intranettet til bedriften din</translation>
 <translation id="3311330810461485557">Søk etter app, dato og andre ting.</translation>
+<translation id="3331974543021145906">Appinformasjon</translation>
 <translation id="3334729583274622784">Vil du endre filetternavnet?</translation>
 <translation id="333863344734218290">Advarer deg om usikre offentlige og private nettsteder</translation>
 <translation id="3341262203274374114">Kan ikke slutte å følge. Noe gikk galt.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_or.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_or.xtb
index d517819a..1c96b0b 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_or.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_or.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">ରିଡିଂ ମୋଡ ସର୍ବଦା ଏଠାରେ ଉପଲବ୍ଧ</translation>
 <translation id="331080746368555063">ପ୍ରାଇଭେଟ ସାଇଟଗୁଡ଼ିକ ଆପଣଙ୍କ କମ୍ପାନୀର ଇଣ୍ଟ୍ରାନେଟ ପରି ଜିନିଷଗୁଡ଼ିକୁ ଅନ୍ତର୍ଭୁକ୍ତ କରିପାରେ</translation>
 <translation id="3311330810461485557">ଆପ, ଡାଟା ଏବଂ ଆହୁରି ଅନେକ କିଛି ଅନୁସାରେ ସର୍ଚ୍ଚ କରନ୍ତୁ।</translation>
+<translation id="3331974543021145906">ଆପ୍ ସୂଚନା</translation>
 <translation id="3334729583274622784">ଫାଇଲ୍‍‍ର ଏକ୍ସଟେନ୍‌ସନ୍ ବଦଳାଇବେ କି?</translation>
 <translation id="333863344734218290">ଅସୁରକ୍ଷିତ ପବ୍ଲିକ ଓ ପ୍ରାଇଭେଟ ସାଇଟଗୁଡ଼ିକ ପାଇଁ ଆପଣଙ୍କୁ ଚେତାବନୀ ଦିଏ</translation>
 <translation id="3341262203274374114">ଅନୁସରଣ କରିବା ବନ୍ଦ କରାଯାଇପାରିବ ନାହିଁ। କିଛି ତ୍ରୁଟି ହୋଇଛି।</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_pa.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_pa.xtb
index f7b0da2..a3008419 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_pa.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_pa.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">ਪੜ੍ਹਨ ਸੰਬੰਧੀ ਮੋਡ ਹਮੇਸ਼ਾਂ ਇੱਥੇ ਉਪਲਬਧ ਹੁੰਦਾ ਹੈ</translation>
 <translation id="331080746368555063">ਨਿੱਜੀ ਸਾਈਟਾਂ ਵਿੱਚ ਤੁਹਾਡੀ ਕੰਪਨੀ ਦੇ ਇੰਟ੍ਰਾਨੈੱਟ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਸ਼ਾਮਲ ਹੋ ਸਕਦੀਆਂ ਹਨ</translation>
 <translation id="3311330810461485557">ਐਪ, ਤਾਰੀਖ ਅਤੇ ਹੋਰ ਚੀਜ਼ਾਂ ਨਾਲ ਖੋਜੋ।</translation>
+<translation id="3331974543021145906">ਐਪ ਜਾਣਕਾਰੀ</translation>
 <translation id="3334729583274622784">ਕੀ ਫ਼ਾਈਲ ਐਕਸਟੈਂਸ਼ਨ ਨੂੰ ਬਦਲਣਾ ਹੈ?</translation>
 <translation id="333863344734218290">ਤੁਹਾਨੂੰ ਅਸੁਰੱਖਿਅਤ ਜਨਤਕ ਅਤੇ ਨਿੱਜੀ ਸਾਈਟਾਂ ਲਈ ਚਿਤਾਵਨੀ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ</translation>
 <translation id="3341262203274374114">ਅਨੁਸਰਣ ਕਰਨਾ ਬੰਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਕੋਈ ਗੜਬੜ ਹੋ ਗਈ।</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_pl.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_pl.xtb
index 747a9239..7cf58d1 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_pl.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_pl.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Tu jest zawsze dostępny tryb czytania</translation>
 <translation id="331080746368555063">Witryny prywatne mogą obejmować np. intranet Twojej firmy</translation>
 <translation id="3311330810461485557">Szukaj według aplikacji, daty i innych kryteriów.</translation>
+<translation id="3331974543021145906">Informacje o aplikacji</translation>
 <translation id="3334729583274622784">Zmienić rozszerzenie pliku?</translation>
 <translation id="333863344734218290">Ostrzegaj przed niebezpiecznymi witrynami publicznymi i prywatnymi</translation>
 <translation id="3341262203274374114">Nie udało się przestać obserwować. Coś poszło nie tak.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_pt-BR.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_pt-BR.xtb
index d4c8108..fd08ad4 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_pt-BR.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_pt-BR.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">O Modo de Leitura está sempre disponível aqui</translation>
 <translation id="331080746368555063">Sites particulares podem incluir, por exemplo, a intranet da sua empresa</translation>
 <translation id="3311330810461485557">Pesquise por app, data e muito mais.</translation>
+<translation id="3331974543021145906">Informações sobre o aplicativo</translation>
 <translation id="3334729583274622784">Mudar extensão do arquivo?</translation>
 <translation id="333863344734218290">Avisa sobre sites públicos e particulares sem segurança</translation>
 <translation id="3341262203274374114">Não foi possível parar de seguir. Algo deu errado.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_pt-PT.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_pt-PT.xtb
index 2bcc9283..cefbf41 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_pt-PT.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_pt-PT.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">O modo de leitura está sempre disponível aqui</translation>
 <translation id="331080746368555063">Os sites privados podem incluir elementos como a intranet da sua empresa</translation>
 <translation id="3311330810461485557">Pesquise por app, data e muito mais.</translation>
+<translation id="3331974543021145906">Informações da aplicação</translation>
 <translation id="3334729583274622784">Quer alterar a extensão de ficheiro?</translation>
 <translation id="333863344734218290">Envia-lhe avisos sobre sites públicos e privados inseguros</translation>
 <translation id="3341262203274374114">Não é possível deixar de seguir. Algo correu mal.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ro.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ro.xtb
index ef3b320c..1d3231e1 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ro.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ro.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Modul de lectură este întotdeauna disponibil aici</translation>
 <translation id="331080746368555063">Site-urile private pot să includă rețeaua intranet a companiei tale</translation>
 <translation id="3311330810461485557">Caută după aplicație, dată și altele.</translation>
+<translation id="3331974543021145906">Informații despre aplicație</translation>
 <translation id="3334729583274622784">Modifici extensia de fișier?</translation>
 <translation id="333863344734218290">Te avertizează cu privire la site-urile publice și private nesigure</translation>
 <translation id="3341262203274374114">Nu se poate anula urmărirea. A apărut o eroare.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ru.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ru.xtb
index 2906b0e..9d9bdce 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ru.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ru.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">Режим чтения всегда доступен здесь</translation>
 <translation id="331080746368555063">К частным сайтам можно отнести, например, те, которые размещены в интранете вашей компании</translation>
 <translation id="3311330810461485557">Поиск по приложению, дате и другим параметрам</translation>
+<translation id="3331974543021145906">О приложении</translation>
 <translation id="3334729583274622784">Изменить расширение имени файла?</translation>
 <translation id="333863344734218290">Предупреждать о незащищенных общедоступных и частных сайтах</translation>
 <translation id="3341262203274374114">Не удалось отменить подписку. Что-то пошло не так.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_si.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_si.xtb
index 1f9d25b..7dc751b 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_si.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_si.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">කියවීමේ මාදිලිය සැම විට ම මෙහි ඇත</translation>
 <translation id="331080746368555063">පුද්ගලික වෙබ් අඩවිවලට ඔබේ සමාගමේ අන්තර්ජාලය වැනි දේවල් ඇතුළත් විය හැක</translation>
 <translation id="3311330810461485557">යෙදුමට, දිනයට, සහ තව ඒවාට අනුව සොයන්න</translation>
+<translation id="3331974543021145906">යෙදුම් තතු</translation>
 <translation id="3334729583274622784">ගොනු දිගුව වෙනස් කරන්නේද?</translation>
 <translation id="333863344734218290">අනාරක්ෂිත පොදු සහ පෞද්ගලික අඩවි සඳහා ඔබට අනතුරු අඟවයි</translation>
 <translation id="3341262203274374114">අනුගමනය කිරීමෙන් ඉවත් වීමට නොහැකිය. යම් දෙයක් වැරදිණි.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sk.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sk.xtb
index 9b72ba1..33d86e0 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sk.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sk.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Režim čítania je vždy k dispozícii tu</translation>
 <translation id="331080746368555063">Súkromné weby môžu zahŕňať rôzny obsah, napríklad intranet vašej firmy</translation>
 <translation id="3311330810461485557">Vyhľadávajte podľa aplikácie, dátumu a ďalších kritérií.</translation>
+<translation id="3331974543021145906">Informácie o aplikácii</translation>
 <translation id="3334729583274622784">Chcete zmeniť príponu súboru?</translation>
 <translation id="333863344734218290">Upozorňuje vás na nezabezpečené verejné a súkromné weby</translation>
 <translation id="3341262203274374114">Nedá sa prestať sledovať. Vyskytol sa problém.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sl.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sl.xtb
index fb4566b..22fd21cc 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sl.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sl.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Način za branje je vedno na voljo tukaj</translation>
 <translation id="331080746368555063">Zasebna spletna mesta lahko vključujejo stvari, kot je intranet podjetja</translation>
 <translation id="3311330810461485557">Iščite po aplikaciji, datumu in drugih možnostih.</translation>
+<translation id="3331974543021145906">Podatki o aplikaciji</translation>
 <translation id="3334729583274622784">Želite spremeniti pripono datoteke?</translation>
 <translation id="333863344734218290">Prikaz opozorila za javna in zasebna spletna mesta, ki niso varna</translation>
 <translation id="3341262203274374114">Spremljanja ni mogoče preklicati. Prišlo je do napake.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sq.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sq.xtb
index ee43d10..316f427 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sq.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sq.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Modaliteti i leximit ofrohet gjithmonë këtu</translation>
 <translation id="331080746368555063">Sajtet private mund të përfshijnë gjëra të tilla si intraneti i kompanisë sate</translation>
 <translation id="3311330810461485557">Kërko sipas aplikacionit, datës etj.</translation>
+<translation id="3331974543021145906">Informacion mbi aplikacionin</translation>
 <translation id="3334729583274622784">Do ta ndryshosh prapashtesën e skedarit?</translation>
 <translation id="333863344734218290">Të paralajmëron për sajtet private dhe publike të pasigurta</translation>
 <translation id="3341262203274374114">Ndjekja nuk mund të anulohet. Ndodhi një gabim.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sr-Latn.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sr-Latn.xtb
index aa3329c..92195b4 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sr-Latn.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sr-Latn.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">Režim čitanja je uvek dostupan ovde</translation>
 <translation id="331080746368555063">Privatni sajtovi mogu da obuhvataju stvari kao što je intranet preduzeća</translation>
 <translation id="3311330810461485557">Pretražujte prema aplikaciji, datumu i drugim opcijama.</translation>
+<translation id="3331974543021145906">Informacije o aplikaciji</translation>
 <translation id="3334729583274622784">Želite da promenite ekstenziju datoteke?</translation>
 <translation id="333863344734218290">Upozorava vas na nebezbedne javne i privatne sajtove</translation>
 <translation id="3341262203274374114">Opozivanje praćenja nije uspelo. Došlo je do greške.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sr.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sr.xtb
index 6e17a4d..754eae4 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sr.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sr.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">Режим читања је увек доступан овде</translation>
 <translation id="331080746368555063">Приватни сајтови могу да обухватају ствари као што је интранет предузећа</translation>
 <translation id="3311330810461485557">Претражујте према апликацији, датуму и другим опцијама.</translation>
+<translation id="3331974543021145906">Информације о апликацији</translation>
 <translation id="3334729583274622784">Желите да промените екстензију датотеке?</translation>
 <translation id="333863344734218290">Упозорава вас на небезбедне јавне и приватне сајтове</translation>
 <translation id="3341262203274374114">Опозивање праћења није успело. Дошло је до грешке.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sv.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sv.xtb
index a2ecdfe..3c283392 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sv.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sv.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Läsläget är alltid tillgängligt här</translation>
 <translation id="331080746368555063">Privata webbplatser kan vara till exempel företagets intranät</translation>
 <translation id="3311330810461485557">Sök efter app, datum med mera.</translation>
+<translation id="3331974543021145906">Appinformation</translation>
 <translation id="3334729583274622784">Vill du ändra filnamnstillägget?</translation>
 <translation id="333863344734218290">Varnar för osäkra offentliga och privata webbplatser</translation>
 <translation id="3341262203274374114">Det gick inte att sluta följa flödet på grund av ett fel.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sw.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sw.xtb
index 7b9abcd..80b9057 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_sw.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_sw.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Hali ya kusoma hupatikana hapa</translation>
 <translation id="331080746368555063">Huenda tovuti za faragha zikajumuisha vitu kama vile intraneti ya kampuni yako</translation>
 <translation id="3311330810461485557">Tafuta kulingana na programu, tarehe na zaidi.</translation>
+<translation id="3331974543021145906">Maelezo ya programu</translation>
 <translation id="3334729583274622784">Ungependa kubadilisha kiendelezi cha faili?</translation>
 <translation id="333863344734218290">Hukutahadharisha kuhusu tovuti za faragha na za umma zisizo salama</translation>
 <translation id="3341262203274374114">Imeshinda kuacha kufuatilia. Hitilafu fulani imetokea.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ta.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ta.xtb
index 7d01cd1..06b25ba 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ta.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ta.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">இங்கு வாசிப்புப் பயன்முறை எப்போதும் கிடைக்கும்</translation>
 <translation id="331080746368555063">உங்கள் நிறுவனத்தின் இன்ட்ரானெட் போன்றவை தனிப்பட்ட தளங்கள் என்று கருதப்படலாம்</translation>
 <translation id="3311330810461485557">ஆப்ஸ், தேதி மற்றும் பலவற்றின் மூலம் தேடுங்கள்.</translation>
+<translation id="3331974543021145906">ஆப்ஸ் தகவல்</translation>
 <translation id="3334729583274622784">ஃபைல் நீட்டிப்பை மாற்றவா?</translation>
 <translation id="333863344734218290">பாதுகாப்பற்ற பொது மற்றும் தனிப்பட்ட தளங்களுக்கு எச்சரிக்கை செய்</translation>
 <translation id="3341262203274374114">பின்தொடர்வதை நிறுத்த முடியவில்லை. ஏதோ தவறாகிவிட்டது.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_te.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_te.xtb
index a207525..ab6f46e4 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_te.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_te.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">రీడింగ్ మోడ్ ఎల్లప్పుడూ ఇక్కడ అందుబాటులో ఉంటుంది</translation>
 <translation id="331080746368555063">ప్రైవేట్ సైట్‌లలో మీ కంపెనీ ఇంట్రానెట్ వంటి విషయాలు ఉండవచ్చు</translation>
 <translation id="3311330810461485557">యాప్, తేదీ, అలాగే మరిన్నింటి ద్వారా సెర్చ్ చేయండి.</translation>
+<translation id="3331974543021145906">యాప్‌ సమాచారం</translation>
 <translation id="3334729583274622784">ఫైల్ ఎక్స్‌టెన్షన్‌ను మార్చాలా?</translation>
 <translation id="333863344734218290">సురక్షితం కాని పబ్లిక్ &amp; ప్రైవేట్ సైట్‌ల విషయంలో మిమ్మల్ని హెచ్చరిస్తుంది</translation>
 <translation id="3341262203274374114">అనుసరణను రద్దు చేయడం సాధ్యపడదు. ఏదో తప్పు జరిగింది.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_th.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_th.xtb
index 39bbeb4..6d8e66ba5 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_th.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_th.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">โหมดการอ่านพร้อมใช้งานเสมอ</translation>
 <translation id="331080746368555063">เว็บไซต์ส่วนตัวอาจรวมถึงอินทราเน็ตของบริษัทของคุณ เป็นต้น</translation>
 <translation id="3311330810461485557">ค้นหาตามแอป วันที่ และอื่นๆ</translation>
+<translation id="3331974543021145906">ข้อมูลแอป</translation>
 <translation id="3334729583274622784">เปลี่ยนนามสกุลไฟล์ใช่ไหม</translation>
 <translation id="333863344734218290">เตือนคุณสำหรับเว็บไซต์สาธารณะและเว็บไซต์ส่วนตัวที่ไม่ปลอดภัย</translation>
 <translation id="3341262203274374114">เลิกติดตามไม่ได้ เกิดข้อผิดพลาด</translation>
@@ -1830,6 +1831,7 @@
 <translation id="9133397713400217035">สำรวจแบบออฟไลน์</translation>
 <translation id="9143389653531441385">ลงชื่อสมัครใช้ด้วย <ph name="IDENTITY_PROVIDER_ETLD_PLUS_ONE" /></translation>
 <translation id="9148126808321036104">ลงชื่อเข้าใช้อีกครั้ง</translation>
+<translation id="9148564897242852377">สลับการเลือกแท็บ</translation>
 <translation id="9157212632995922070">เลือกบัญชีเพื่อดำเนินการต่อไปยัง <ph name="SITE_ETLD_PLUS_ONE" /></translation>
 <translation id="9158770349521403363">แชร์เนื้อหาเท่านั้น</translation>
 <translation id="9161172953291434881">คุณไว้ใจให้เว็บไซต์นี้เข้าถึงข้อมูลของคุณไหม</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_tr.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_tr.xtb
index c2e05eed..acbc9cd3 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_tr.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_tr.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Okuma Modu buradan her zaman kullanılabilir</translation>
 <translation id="331080746368555063">Gizli siteler, şirketinizin intranet'i gibi siteleri içerebilir</translation>
 <translation id="3311330810461485557">Uygulama, tarih ve diğer ölçütlere göre arama yapın.</translation>
+<translation id="3331974543021145906">Uygulama bilgisi</translation>
 <translation id="3334729583274622784">Dosya uzantısı değiştirilsin mi?</translation>
 <translation id="333863344734218290">Sizi herkese açık ve gizli durumdaki güvensiz sitelerle ilgili uyarır</translation>
 <translation id="3341262203274374114">Takip bırakılamıyor. Bir sorun oldu.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_uk.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_uk.xtb
index e43d190..5241c7d9 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_uk.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_uk.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Ви можете будь-коли ввімкнути режим читання тут</translation>
 <translation id="331080746368555063">До приватних сайтів може належати, наприклад, інтранет вашої компанії</translation>
 <translation id="3311330810461485557">Шукайте за додатком, датою тощо.</translation>
+<translation id="3331974543021145906">Про додаток</translation>
 <translation id="3334729583274622784">Змінити розширення файлу?</translation>
 <translation id="333863344734218290">Попереджати про небезпечні загальнодоступні й приватні сайти</translation>
 <translation id="3341262203274374114">Не вдалося скасувати підписку. Сталася помилка.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ur.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ur.xtb
index e2a70f7b..f643f54 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ur.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ur.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">ریڈنگ موڈ ہمیشہ یہاں دستیاب ہے</translation>
 <translation id="331080746368555063">نجی سائٹس میں آپ کی کمپنی کے انٹرانیٹ جیسی چیزیں شامل ہو سکتی ہیں</translation>
 <translation id="3311330810461485557">ایپ، تاریخ اور مزید کے ذریعے تلاش کریں۔</translation>
+<translation id="3331974543021145906">ایپ کی معلومات</translation>
 <translation id="3334729583274622784">فائل ایکسٹینشن تبدیل کریں؟</translation>
 <translation id="333863344734218290">آپ کو غیر محفوظ عوامی اور نجی سائٹس کے لیے وارننگ حاصل ہوتی ہے</translation>
 <translation id="3341262203274374114">پیروی ختم نہیں کر سکتے۔ کچھ غلط ہو گیا۔</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_uz.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_uz.xtb
index dfec406..73b3054 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_uz.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_uz.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">Bu sahifada doim Mutolaa rejimi mavjud</translation>
 <translation id="331080746368555063">Maxfiy saytlarga kompaniyalar intraneti kabi tizimlar kiradi</translation>
 <translation id="3311330810461485557">Ilova, sana va boshqa parametrlar asosida qidirish.</translation>
+<translation id="3331974543021145906">Ilova haqida</translation>
 <translation id="3334729583274622784">Fayl kengaytmasi oʻzgartirilsinmi?</translation>
 <translation id="333863344734218290">Himoyasiz ochiq va maxfiy saytlar haqida ogohlantirish</translation>
 <translation id="3341262203274374114">Obuna bekor qilinmadi. Xatolik yuz berdi.</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_vi.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_vi.xtb
index 15ba2cf..31d43c3 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_vi.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_vi.xtb
@@ -159,6 +159,7 @@
 <translation id="1726477445370128854">Bạn có thể duyệt và tìm kiếm nhanh hơn khi Chrome tải trước các trang mà trình duyệt cho rằng bạn có thể sẽ truy cập</translation>
 <translation id="1728803206919861584">Lưu khoá truy cập ngoài chế độ Ẩn danh?</translation>
 <translation id="1732514748744806741">Nếu mật khẩu của bạn bị lộ, chúng tôi sẽ thông báo cho bạn</translation>
+<translation id="1737694586321018892">Cấp bảo mật mạnh nhất của Chrome</translation>
 <translation id="1747593111377567311">{NUM_SITES,plural, =1{Gần đây, có 1 trang web đã gửi nhiều thông báo}other{Gần đây, có # trang web đã gửi nhiều thông báo}}</translation>
 <translation id="1749561566933687563">Đồng bộ hóa dấu trang của bạn</translation>
 <translation id="1750238553597293878">Tiếp tục sử dụng mật khẩu trong Tài khoản Google</translation>
@@ -414,6 +415,7 @@
 <translation id="2909615210195135082">Nền tảng thông báo của Google</translation>
 <translation id="2912345083818861431">Dùng phương thức khóa màn hình để xem các thẻ ẩn danh đang mở</translation>
 <translation id="2918484639460781603">Chuyển đến phần cài đặt</translation>
+<translation id="2920776558188188922">Di chuyển thanh địa chỉ lên trên cùng hoặc xuống dưới cùng để có trải nghiệm duyệt web phù hợp với nhu cầu riêng</translation>
 <translation id="2923908459366352541">Tên không hợp lệ</translation>
 <translation id="2932150158123903946">Bộ nhớ Google <ph name="APP_NAME" /></translation>
 <translation id="2932222164150889403">Bàn phím của bạn sẽ không thay đổi</translation>
@@ -502,6 +504,7 @@
 <translation id="3309978850836730073">Chế độ đọc luôn có tại đây</translation>
 <translation id="331080746368555063">Các trang web riêng tư có thể bao gồm những nội dung như mạng nội bộ của công ty bạn</translation>
 <translation id="3311330810461485557">Tìm kiếm theo ứng dụng, ngày, v.v.</translation>
+<translation id="3331974543021145906">Thông tin ứng dụng</translation>
 <translation id="3334729583274622784">Thay đổi đuôi tệp?</translation>
 <translation id="333863344734218290">Cảnh báo bạn về các trang web công khai và riêng tư không an toàn</translation>
 <translation id="3341262203274374114">Không thể ngừng theo dõi. Đã xảy ra lỗi.</translation>
@@ -973,6 +976,7 @@
 <translation id="549660026907543296">Tham gia dưới tên <ph name="MEMBER_FULL_NAME" /></translation>
 <translation id="5517326820028369152">Bảng hoạt động gần đây ở dưới cùng</translation>
 <translation id="5526281268548144413">Không đóng được trên nhiều cửa sổ</translation>
+<translation id="5532536403031755365">Bổ sung một lớp bảo vệ trước các mối đe doạ trên mạng bằng chế độ bảo vệ nâng cao</translation>
 <translation id="5548606607480005320">Kiểm tra an toàn</translation>
 <translation id="5554520618550346933">Khi bạn dùng một mật khẩu đã bị công khai trên mạng, Chrome sẽ cảnh báo cho bạn. Trong quá trình này, mật khẩu và tên người dùng của bạn được mã hoá nên không ai đọc được, kể cả Google.</translation>
 <translation id="5555525474779371165">Chọn chế độ bảo vệ bằng tính năng Duyệt web an toàn của bạn</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_zh-CN.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_zh-CN.xtb
index 1a2a2126..6bceb24a 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_zh-CN.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_zh-CN.xtb
@@ -504,6 +504,7 @@
 <translation id="3309978850836730073">随时可在此处找到阅读模式</translation>
 <translation id="331080746368555063">私密网站可能包括公司内网等</translation>
 <translation id="3311330810461485557">按应用、日期等进行搜索。</translation>
+<translation id="3331974543021145906">应用信息</translation>
 <translation id="3334729583274622784">更改文件扩展名?</translation>
 <translation id="333863344734218290">在您访问不安全的公共网站和私有网站时发出警告</translation>
 <translation id="3341262203274374114">无法取消关注。出了点问题。</translation>
@@ -975,7 +976,7 @@
 <translation id="549660026907543296">以<ph name="MEMBER_FULL_NAME" />的身份加入</translation>
 <translation id="5517326820028369152">显示近期活动的底部动作条</translation>
 <translation id="5526281268548144413">无法在多个窗口中关闭</translation>
-<translation id="5532536403031755365">借助增强型保护功能,再添一道安全防线,有效抵御在线威胁</translation>
+<translation id="5532536403031755365">借助增强型保护功能,再添一层保障,有效抵御在线威胁</translation>
 <translation id="5548606607480005320">安全检查</translation>
 <translation id="5554520618550346933">如果 Chrome 发现您使用的某个密码已被发布到网上,就会发出警告。在这个过程中,您的密码和用户名始终加密,任何人员/任何一方(包括 Google)都无法读取。</translation>
 <translation id="5555525474779371165">选择所需的“安全浏览”功能保护级别</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_zh-HK.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_zh-HK.xtb
index 033d4b2..99e1a76 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_zh-HK.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_zh-HK.xtb
@@ -159,6 +159,7 @@
 <translation id="1726477445370128854">Chrome 會預先載入您可能瀏覽的網頁,加快瀏覽及搜尋速度</translation>
 <translation id="1728803206919861584">要在無痕模式外儲存密鑰嗎?</translation>
 <translation id="1732514748744806741">如果你的密碼被盜用,我們會通知你</translation>
+<translation id="1737694586321018892">取得 Chrome 最強大的安全防護</translation>
 <translation id="1747593111377567311">{NUM_SITES,plural, =1{1 個網站最近曾傳送大量通知}other{# 個網站最近曾傳送大量通知}}</translation>
 <translation id="1749561566933687563">同步您的書籤</translation>
 <translation id="1750238553597293878">繼續使用 Google 帳戶中的密碼</translation>
@@ -414,6 +415,7 @@
 <translation id="2909615210195135082">Google 通知平台</translation>
 <translation id="2912345083818861431">透過螢幕鎖定查看開啟的無痕式分頁</translation>
 <translation id="2918484639460781603">前往設定</translation>
+<translation id="2920776558188188922">將網址列的位置改為頁底的最上方,自訂瀏覽體驗</translation>
 <translation id="2923908459366352541">名稱無效</translation>
 <translation id="2932150158123903946">Google <ph name="APP_NAME" /> 儲存空間</translation>
 <translation id="2932222164150889403">這不會影響您的鍵盤設定</translation>
@@ -502,6 +504,7 @@
 <translation id="3309978850836730073">你可隨時在此開啟閱讀模式</translation>
 <translation id="331080746368555063">私人網站可能包括公司內部網絡等項目</translation>
 <translation id="3311330810461485557">按應用程式、日期等條件搜尋。</translation>
+<translation id="3331974543021145906">應用程式資料</translation>
 <translation id="3334729583274622784">要變更副檔名嗎?</translation>
 <translation id="333863344734218290">在你瀏覽不安全的公開和私人網站時發出警告</translation>
 <translation id="3341262203274374114">無法取消追蹤,發生錯誤。</translation>
@@ -973,6 +976,7 @@
 <translation id="549660026907543296">以<ph name="MEMBER_FULL_NAME" />的身分加入</translation>
 <translation id="5517326820028369152">近期活動頁底面板</translation>
 <translation id="5526281268548144413">無法關閉多個視窗中的分頁</translation>
+<translation id="5532536403031755365">強化保護功能為對抗網上威脅增添多一重安全保障</translation>
 <translation id="5548606607480005320">安全檢查</translation>
 <translation id="5554520618550346933">使用密碼時,如果密碼已在網上發佈,Chrome 將會提醒你。核對時,您的密碼和使用者名稱將會加密,任何人 (包括 Google) 均無法讀取。</translation>
 <translation id="5555525474779371165">選取您的「安全瀏覽」功能</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_zh-TW.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_zh-TW.xtb
index 5655ecc..adb8e29 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_zh-TW.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_zh-TW.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">隨時可在此使用閱讀模式</translation>
 <translation id="331080746368555063">私人網站可能會包含貴公司的內部網路等項目</translation>
 <translation id="3311330810461485557">依應用程式、日期等條件搜尋。</translation>
+<translation id="3331974543021145906">應用程式資訊</translation>
 <translation id="3334729583274622784">是否要變更副檔名?</translation>
 <translation id="333863344734218290">在你造訪不安全的公開和私人網站時發出警告</translation>
 <translation id="3341262203274374114">無法取消追蹤,發生錯誤。</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_zu.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_zu.xtb
index 71a9d0a..1b26063 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_zu.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_zu.xtb
@@ -502,6 +502,7 @@
 <translation id="3309978850836730073">Imodi yokufunda ihlala itholakala lapha</translation>
 <translation id="331080746368555063">Amasayithi ayimfihlo angase ahlanganise izinto ezinjenge-intanethi yangaphakathi yenkampani yakho</translation>
 <translation id="3311330810461485557">Sesha nge-app, usuku, nokuningi.</translation>
+<translation id="3331974543021145906">Ulwazi lohlelo lokusebenza</translation>
 <translation id="3334729583274622784">Shintsha isandiso sefayela?</translation>
 <translation id="333863344734218290">Iyakuxwayisa kumasayithi angavikelekile omphakathi nayimfihlo</translation>
 <translation id="3341262203274374114">Ayikwazi ukuyeka ukulandela. Kunento engahambanga kahle.</translation>
diff --git a/chrome/browser/ui/android/tab_model/tab_model_observer_jni_bridge.cc b/chrome/browser/ui/android/tab_model/tab_model_observer_jni_bridge.cc
index fb87dda..3320175d 100644
--- a/chrome/browser/ui/android/tab_model/tab_model_observer_jni_bridge.cc
+++ b/chrome/browser/ui/android/tab_model/tab_model_observer_jni_bridge.cc
@@ -44,6 +44,9 @@
   for (auto& observer : model_observers_) {
     observer.DidSelectTab(tab, static_cast<TabModel::TabSelectionType>(type));
   }
+  for (auto& observer : interface_observers_) {
+    observer.OnActiveTabChanged(tab);
+  }
 }
 
 void TabModelObserverJniBridge::WillCloseTab(JNIEnv* env, TabAndroid* tab) {
diff --git a/chrome/browser/ui/android/theme/BUILD.gn b/chrome/browser/ui/android/theme/BUILD.gn
index adf1576e..bb8036c6 100644
--- a/chrome/browser/ui/android/theme/BUILD.gn
+++ b/chrome/browser/ui/android/theme/BUILD.gn
@@ -7,7 +7,6 @@
 android_library("java") {
   sources = [
     "java/src/org/chromium/chrome/browser/theme/BottomUiThemeColorProvider.java",
-    "java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java",
     "java/src/org/chromium/chrome/browser/theme/ThemeColorProvider.java",
     "java/src/org/chromium/chrome/browser/theme/ThemeModuleUtils.java",
     "java/src/org/chromium/chrome/browser/theme/ThemeUtils.java",
@@ -53,7 +52,6 @@
 robolectric_library("junit") {
   sources = [
     "java/src/org/chromium/chrome/browser/theme/BottomUiThemeColorProviderTest.java",
-    "java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java",
     "java/src/org/chromium/chrome/browser/theme/ThemeUtilsUnitTest.java",
   ]
   deps = [
diff --git a/chrome/browser/ui/android/theme/java/res/values/colors.xml b/chrome/browser/ui/android/theme/java/res/values/colors.xml
index c00a1eec..ed88e48 100644
--- a/chrome/browser/ui/android/theme/java/res/values/colors.xml
+++ b/chrome/browser/ui/android/theme/java/res/values/colors.xml
@@ -18,8 +18,6 @@
     <color name="pane_switcher_background_incognito">@color/gm3_baseline_surface_container_dark</color>
     <color name="pane_switcher_selected_tab_incognito">@color/gm3_baseline_surface_bright_dark</color>
 
-    <color name="incognito_tab_action_button_color">@color/baseline_neutral_variant_80</color>
-
     <color name="incognito_hub_search_box_bg_color">@color/gm3_baseline_surface_container_highest_dark</color>
 
 </resources>
diff --git a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
deleted file mode 100644
index 9147d8cf..0000000
--- a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.theme;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-
-import androidx.annotation.ColorInt;
-import androidx.appcompat.content.res.AppCompatResources;
-import androidx.core.content.ContextCompat;
-
-import com.google.android.material.color.MaterialColors;
-
-import org.chromium.build.annotations.NullMarked;
-import org.chromium.build.annotations.Nullable;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.components.browser_ui.styles.SemanticColorUtils;
-import org.chromium.components.tab_groups.TabGroupColorId;
-import org.chromium.components.tab_groups.TabGroupColorPickerUtils;
-
-/** Utility class that provides color values based on feature flags enabled. */
-@NullMarked
-public class SurfaceColorUpdateUtils {
-    private static final String TAG = "SurfaceColorUpdateUtils";
-
-    /** Whether enable the containment on the tab group list pane. */
-    public static boolean isTabGroupListContainmentEnabled() {
-        return ChromeFeatureList.sGridTabSwitcherSurfaceColorUpdate.isEnabled()
-                && ChromeFeatureList.sTabGroupListContainment.getValue();
-    }
-
-    /** Whether new GM3 colors are being used for the tab group colors. */
-    public static boolean useNewGm3GtsTabGroupColors() {
-        return ChromeFeatureList.sAndroidTabGroupsColorUpdateGm3.isEnabled()
-                || ThemeModuleUtils.isForceEnableDependencies();
-    }
-
-    /**
-     * Returns the placeholder color for the card view in grid tab switcher on the enabled flag and
-     * incognito.
-     *
-     * @param context {@link Context} used to retrieve colors.
-     * @param isIncognito Whether the color is used for incognito mode.
-     * @param colorId Color chosen by user for the TabGroup, null if not a tab group.
-     * @return The mini-thumbnail placeholder color.
-     */
-    public static @ColorInt int getCardViewMiniThumbnailPlaceholderColor(
-            Context context, boolean isIncognito, @Nullable @TabGroupColorId Integer colorId) {
-        if (useNewGm3GtsTabGroupColors() && colorId != null) {
-            return TabGroupColorPickerUtils.getTabGroupCardMiniThumbnailPlaceholderColor(
-                    context, colorId, isIncognito);
-        }
-        if (isIncognito) {
-            return context.getColor(R.color.incognito_tab_thumbnail_placeholder_color);
-        }
-        return SemanticColorUtils.getColorSurfaceContainerLow(context);
-    }
-
-    /**
-     * Returns the text color used for the card view in grid tab switcher on the enabled flag and
-     * incognito.
-     *
-     * @param context {@link Context} used to retrieve colors.
-     * @param isIncognito Whether the color is used for incognito mode.
-     * @param colorId Color chosen by user for the TabGroup, null if not a tab group.
-     * @return The text color for the number used on the tab group cards.
-     */
-    public static @ColorInt int getCardViewGroupNumberTextColor(
-            Context context, boolean isIncognito, @Nullable @TabGroupColorId Integer colorId) {
-        if (useNewGm3GtsTabGroupColors() && colorId != null) {
-            return TabGroupColorPickerUtils.getTabGroupCardTextColor(context, colorId, isIncognito);
-        }
-        return isIncognito
-                ? context.getColor(R.color.incognito_tab_tile_number_color)
-                : SemanticColorUtils.getDefaultTextColor(context);
-    }
-
-    /**
-     * Returns the text color used for the card view in grid tab switcher on the enabled flag and
-     * incognito.
-     *
-     * @param context {@link Context} used to retrieve colors.
-     * @param isIncognito Whether the color is used for incognito mode.
-     * @param colorId Color chosen by user for the TabGroup, null if not a tab group.
-     * @return The text color for the number used on the tab group cards.
-     */
-    public static ColorStateList getCardViewActionButtonColor(
-            Context context, boolean isIncognito, @Nullable @TabGroupColorId Integer colorId) {
-        if (useNewGm3GtsTabGroupColors() && colorId != null) {
-            return ColorStateList.valueOf(
-                    TabGroupColorPickerUtils.getTabGroupCardTextColor(
-                            context, colorId, isIncognito));
-        }
-        return isIncognito
-                ? AppCompatResources.getColorStateList(
-                        context, R.color.incognito_tab_action_button_color)
-                : ColorStateList.valueOf(
-                        MaterialColors.getColor(context, R.attr.colorOnSurfaceVariant, TAG));
-    }
-
-    /**
-     * Returns the color selected icons in hub pane switcher, based on the enabled flag and
-     * incognito.
-     *
-     * @param context {@link Context} used to retrieve colors.
-     * @param isIncognito Whether the color is used for incognito mode.
-     * @param isGtsUpdateEnabled Whether GTS display update is enforced or not.
-     */
-    public static @ColorInt int getHubPaneSwitcherSelectedIconColor(
-            Context context, boolean isIncognito, boolean isGtsUpdateEnabled) {
-        if (isGtsUpdateEnabled) {
-            return isIncognito
-                    ? ContextCompat.getColor(context, R.color.default_icon_color_light)
-                    : SemanticColorUtils.getDefaultIconColor(context);
-        }
-
-        return isIncognito
-                ? ContextCompat.getColor(context, R.color.default_control_color_active_dark)
-                : SemanticColorUtils.getDefaultIconColorAccent1(context);
-    }
-}
diff --git a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java
deleted file mode 100644
index 1613cf53..0000000
--- a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java
+++ /dev/null
@@ -1,308 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.theme;
-
-import static org.junit.Assert.assertEquals;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.view.ContextThemeWrapper;
-
-import androidx.annotation.ColorInt;
-import androidx.appcompat.content.res.AppCompatResources;
-import androidx.core.content.ContextCompat;
-
-import com.google.android.material.color.MaterialColors;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.ContextUtils;
-import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Features.DisableFeatures;
-import org.chromium.base.test.util.Features.EnableFeatures;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.components.browser_ui.styles.SemanticColorUtils;
-import org.chromium.components.tab_groups.TabGroupColorId;
-import org.chromium.components.tab_groups.TabGroupColorPickerUtils;
-
-@RunWith(BaseRobolectricTestRunner.class)
-public class SurfaceColorUpdateUtilsUnitTest {
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        mContext =
-                new ContextThemeWrapper(
-                        ContextUtils.getApplicationContext(), R.style.Theme_BrowserUI_DayNight);
-    }
-
-    @Test
-    @EnableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
-    public void testGetCardViewGroupNumberTextColor_gm3FlagEnabled_withColorId() {
-        @TabGroupColorId int testColorId = TabGroupColorId.CYAN;
-
-        // Test non-incognito.
-        @ColorInt
-        int expectedNonIncognito =
-                TabGroupColorPickerUtils.getTabGroupCardTextColor(
-                        mContext, testColorId, /* isIncognito= */ false);
-        @ColorInt
-        int actualNonIncognito =
-                SurfaceColorUpdateUtils.getCardViewGroupNumberTextColor(
-                        mContext, /* isIncognito= */ false, testColorId);
-        assertEquals(
-                "Group number text color mismatch for non-incognito with GM3 flag and colorId.",
-                expectedNonIncognito,
-                actualNonIncognito);
-
-        // Test incognito.
-        @ColorInt
-        int expectedIncognito =
-                TabGroupColorPickerUtils.getTabGroupCardTextColor(
-                        mContext, testColorId, /* isIncognito= */ true);
-        @ColorInt
-        int actualIncognito =
-                SurfaceColorUpdateUtils.getCardViewGroupNumberTextColor(
-                        mContext, /* isIncognito= */ true, testColorId);
-        assertEquals(
-                "Group number text color mismatch for incognito with GM3 flag and colorId.",
-                expectedIncognito,
-                actualIncognito);
-    }
-
-    @Test
-    @DisableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
-    public void testGetCardViewGroupNumberTextColor_gm3FlagDisabled() {
-
-        @TabGroupColorId int testColorId = TabGroupColorId.PINK;
-
-        // Test non-incognito (fallback path).
-        @ColorInt int expectedNonIncognito = SemanticColorUtils.getDefaultTextColor(mContext);
-        @ColorInt
-        int actualNonIncognito =
-                SurfaceColorUpdateUtils.getCardViewGroupNumberTextColor(
-                        mContext, /* isIncognito= */ false, testColorId);
-        assertEquals(
-                "Group number text color mismatch for non-incognito with GM3 flag disabled (colorId"
-                        + " ignored).",
-                expectedNonIncognito,
-                actualNonIncognito);
-
-        // Test incognito (fallback path).
-        @ColorInt
-        int expectedIncognito =
-                ContextCompat.getColor(mContext, R.color.incognito_tab_tile_number_color);
-        @ColorInt
-        int actualIncognito =
-                SurfaceColorUpdateUtils.getCardViewGroupNumberTextColor(
-                        mContext, /* isIncognito= */ true, testColorId);
-        assertEquals(
-                "Group number text color mismatch for incognito with GM3 flag disabled (colorId"
-                        + " ignored).",
-                expectedIncognito,
-                actualIncognito);
-
-        // Test with a null colorId to ensure it behaves the same as with a non-null colorId when
-        // the flag is off.
-        @ColorInt
-        int actualNonIncognitoNullId =
-                SurfaceColorUpdateUtils.getCardViewGroupNumberTextColor(
-                        mContext, /* isIncognito= */ false, /* colorId= */ null);
-        assertEquals(
-                "Group number text color should be the same for null and non-null colorId when GM3"
-                        + " flag is disabled.",
-                expectedNonIncognito,
-                actualNonIncognitoNullId);
-    }
-
-    @Test
-    @EnableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
-    public void testGetCardViewMiniThumbnailPlaceholderColor_gm3FlagEnabled_withColorId() {
-        @TabGroupColorId int testColorId = TabGroupColorId.PURPLE;
-
-        // Test non-incognito.
-        @ColorInt
-        int expectedNonIncognito =
-                TabGroupColorPickerUtils.getTabGroupCardMiniThumbnailPlaceholderColor(
-                        mContext, testColorId, /* isIncognito= */ false);
-        @ColorInt
-        int actualNonIncognito =
-                SurfaceColorUpdateUtils.getCardViewMiniThumbnailPlaceholderColor(
-                        mContext, /* isIncognito= */ false, testColorId);
-        assertEquals(
-                "Placeholder color mismatch for non-incognito with GM3 flag and colorId.",
-                expectedNonIncognito,
-                actualNonIncognito);
-
-        // Test incognito.
-        @ColorInt
-        int expectedIncognito =
-                TabGroupColorPickerUtils.getTabGroupCardMiniThumbnailPlaceholderColor(
-                        mContext, testColorId, /* isIncognito= */ true);
-        @ColorInt
-        int actualIncognito =
-                SurfaceColorUpdateUtils.getCardViewMiniThumbnailPlaceholderColor(
-                        mContext, /* isIncognito= */ true, testColorId);
-        assertEquals(
-                "Placeholder color mismatch for incognito with GM3 flag and colorId.",
-                expectedIncognito,
-                actualIncognito);
-    }
-
-    @Test
-    @DisableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
-    public void testGetCardViewMiniThumbnailPlaceholderColor_gm3FlagDisabled() {
-
-        @TabGroupColorId int testColorId = TabGroupColorId.ORANGE;
-
-        // Test non-incognito (fallback path).
-        @ColorInt
-        int expectedNonIncognito = SemanticColorUtils.getColorSurfaceContainerLow(mContext);
-        @ColorInt
-        int actualNonIncognito =
-                SurfaceColorUpdateUtils.getCardViewMiniThumbnailPlaceholderColor(
-                        mContext, /* isIncognito= */ false, testColorId);
-        assertEquals(
-                "Placeholder color mismatch for non-incognito with GM3 flag disabled (colorId"
-                        + " ignored).",
-                expectedNonIncognito,
-                actualNonIncognito);
-
-        // Test incognito (fallback path).
-        @ColorInt
-        int expectedIncognito =
-                ContextCompat.getColor(mContext, R.color.incognito_tab_thumbnail_placeholder_color);
-        @ColorInt
-        int actualIncognito =
-                SurfaceColorUpdateUtils.getCardViewMiniThumbnailPlaceholderColor(
-                        mContext, /* isIncognito= */ true, testColorId);
-        assertEquals(
-                "Placeholder color mismatch for incognito with GM3 flag disabled (colorId"
-                        + " ignored).",
-                expectedIncognito,
-                actualIncognito);
-
-        // Test with a null colorId to ensure it behaves the same as with a non-null colorId when
-        // the flag is off.
-        @ColorInt
-        int actualNonIncognitoNullId =
-                SurfaceColorUpdateUtils.getCardViewMiniThumbnailPlaceholderColor(
-                        mContext, /* isIncognito= */ false, /* colorId= */ null);
-        assertEquals(
-                "Placeholder color should be the same for null and non-null colorId when GM3 flag"
-                        + " is disabled.",
-                expectedNonIncognito,
-                actualNonIncognitoNullId);
-    }
-
-    @Test
-    @EnableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
-    public void testGetCardViewActionButtonColor_gm3FlagEnabled_withColorId() {
-        @TabGroupColorId int testColorId = TabGroupColorId.RED;
-
-        // Test non-incognito.
-        ColorStateList expectedNonIncognito =
-                ColorStateList.valueOf(
-                        TabGroupColorPickerUtils.getTabGroupCardTextColor(
-                                mContext, testColorId, /* isIncognito= */ false));
-        ColorStateList actualNonIncognito =
-                SurfaceColorUpdateUtils.getCardViewActionButtonColor(
-                        mContext, /* isIncognito= */ false, testColorId);
-        assertEquals(
-                "Action button color mismatch for non-incognito with GM3 flag and colorId.",
-                expectedNonIncognito,
-                actualNonIncognito);
-
-        // Test incognito.
-        ColorStateList expectedIncognito =
-                ColorStateList.valueOf(
-                        TabGroupColorPickerUtils.getTabGroupCardTextColor(
-                                mContext, testColorId, /* isIncognito= */ true));
-        ColorStateList actualIncognito =
-                SurfaceColorUpdateUtils.getCardViewActionButtonColor(
-                        mContext, /* isIncognito= */ true, testColorId);
-        assertEquals(
-                "Action button color mismatch for incognito with GM3 flag and colorId.",
-                expectedIncognito,
-                actualIncognito);
-    }
-
-    @Test
-    @EnableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
-    public void testGetCardViewActionButtonColor_gm3FlagEnabled_colorIdNull() {
-        // When the GM3 flag is enabled but the colorId is null, the function should
-        // execute its fallback logic.
-
-        // Test non-incognito with a null colorId.
-        ColorStateList expectedNonIncognito =
-                ColorStateList.valueOf(
-                        MaterialColors.getColor(mContext, R.attr.colorOnSurfaceVariant, ""));
-        ColorStateList actualNonIncognito =
-                SurfaceColorUpdateUtils.getCardViewActionButtonColor(
-                        mContext, /* isIncognito= */ false, /* colorId= */ null);
-        assertEquals(
-                "Action button color should use fallback for non-incognito when colorId is null.",
-                expectedNonIncognito,
-                actualNonIncognito);
-
-        // Test incognito with a null colorId.
-        ColorStateList expectedIncognito =
-                AppCompatResources.getColorStateList(
-                        mContext, R.color.incognito_tab_action_button_color);
-        ColorStateList actualIncognito =
-                SurfaceColorUpdateUtils.getCardViewActionButtonColor(
-                        mContext, /* isIncognito= */ true, /* colorId= */ null);
-        assertEquals(
-                "Action button color should use fallback for incognito when colorId is null.",
-                expectedIncognito,
-                actualIncognito);
-    }
-
-    @Test
-    @DisableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
-    public void testGetCardViewActionButtonColor_gm3FlagDisabled() {
-        // If the GM3 flag is disabled, the colorId should be ignored and the fallback logic used.
-        @TabGroupColorId int testColorId = TabGroupColorId.GREEN;
-
-        // Test non-incognito (fallback path).
-        ColorStateList expectedNonIncognito =
-                ColorStateList.valueOf(
-                        MaterialColors.getColor(mContext, R.attr.colorOnSurfaceVariant, ""));
-        ColorStateList actualNonIncognito =
-                SurfaceColorUpdateUtils.getCardViewActionButtonColor(
-                        mContext, /* isIncognito= */ false, testColorId);
-        assertEquals(
-                "Action button color mismatch for non-incognito with GM3 flag disabled (colorId"
-                        + " ignored).",
-                expectedNonIncognito,
-                actualNonIncognito);
-
-        // Test incognito (fallback path).
-        ColorStateList expectedIncognito =
-                AppCompatResources.getColorStateList(
-                        mContext, R.color.incognito_tab_action_button_color);
-        ColorStateList actualIncognito =
-                SurfaceColorUpdateUtils.getCardViewActionButtonColor(
-                        mContext, /* isIncognito= */ true, testColorId);
-        assertEquals(
-                "Action button color mismatch for incognito with GM3 flag disabled (colorId"
-                        + " ignored).",
-                expectedIncognito,
-                actualIncognito);
-
-        // Test with a null colorId to ensure it behaves the same as with a non-null colorId when
-        // the flag is off.
-        ColorStateList actualNonIncognitoNullId =
-                SurfaceColorUpdateUtils.getCardViewActionButtonColor(
-                        mContext, /* isIncognito= */ false, /* colorId= */ null);
-        assertEquals(
-                "Action button color should be the same for null and non-null colorId when GM3 flag"
-                        + " is disabled.",
-                expectedNonIncognito,
-                actualNonIncognitoNullId);
-    }
-}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
index 9aba5c97..047beae 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
@@ -30,7 +30,6 @@
 import org.chromium.chrome.browser.omnibox.ChromeAutocompleteSchemeClassifier;
 import org.chromium.chrome.browser.omnibox.LocationBarDataProvider;
 import org.chromium.chrome.browser.omnibox.NewTabPageDelegate;
-import org.chromium.chrome.browser.omnibox.SearchEngineUtils;
 import org.chromium.chrome.browser.omnibox.UrlBarData;
 import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
 import org.chromium.chrome.browser.paint_preview.TabbedPaintPreview;
@@ -710,11 +709,7 @@
             return R.drawable.omnibox_info;
         }
 
-        boolean skipIconForNeutralState =
-                (mProfile != null
-                                && !SearchEngineUtils.getForProfile(mProfile)
-                                        .shouldShowSearchEngineLogo())
-                        || mNtpDelegate.isCurrentlyVisible();
+        boolean skipIconForNeutralState = mNtpDelegate.isCurrentlyVisible();
 
         return SecurityStatusIcon.getSecurityIconResource(
                 securityLevel,
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonController.java
index 434c747..18dc124 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonController.java
@@ -12,9 +12,12 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.Callback;
 import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.ConfigurationChangedObserver;
 import org.chromium.chrome.browser.tab.Tab;
@@ -22,6 +25,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.R;
 import org.chromium.chrome.browser.toolbar.optional_button.BaseButtonDataProvider;
+import org.chromium.chrome.browser.toolbar.top.tab_strip.StripVisibilityState;
 import org.chromium.chrome.browser.user_education.IphCommandBuilder;
 import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter.HighlightParams;
 import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter.HighlightShape;
@@ -83,6 +87,9 @@
 
     private boolean mIsTablet;
     private final ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
+    private final ObservableSupplier<@StripVisibilityState Integer> mTabStripVisibilitySupplier;
+    private final Callback<Integer> mOnTabStripVisibilityStateChanged =
+            this::onTabStripVisibilityStateChanged;
 
     /**
      * Creates {@code OptionalNewTabButtonController}.
@@ -94,6 +101,7 @@
      * @param tabCreatorManagerSupplier Used to open new tabs.
      * @param activeTabSupplier Used to access the current tab.
      * @param trackerSupplier Supplier for the current profile tracker.
+     * @param tabStripVisibilitySupplier Supplier for the visibility of the tab strip.
      */
     public OptionalNewTabButtonController(
             Context context,
@@ -101,7 +109,8 @@
             ActivityLifecycleDispatcher activityLifecycleDispatcher,
             Supplier<@Nullable TabCreatorManager> tabCreatorManagerSupplier,
             Supplier<@Nullable Tab> activeTabSupplier,
-            Supplier<@Nullable Tracker> trackerSupplier) {
+            Supplier<@Nullable Tracker> trackerSupplier,
+            ObservableSupplier<@StripVisibilityState Integer> tabStripVisibilitySupplier) {
         super(
                 activeTabSupplier,
                 /* modalDialogManager= */ null,
@@ -121,6 +130,10 @@
         mActivityLifecycleDispatcher.register(this);
 
         mIsTablet = DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext);
+        mTabStripVisibilitySupplier = tabStripVisibilitySupplier;
+        if (ChromeFeatureList.sToolbarTabletResizeRefactor.isEnabled()) {
+            mTabStripVisibilitySupplier.addObserver(mOnTabStripVisibilityStateChanged);
+        }
     }
 
     @Override
@@ -141,10 +154,15 @@
         }
     }
 
+    private void onTabStripVisibilityStateChanged(@StripVisibilityState int tabStripVisibility) {
+        mButtonData.setCanShow(shouldShowButton(mActiveTabSupplier.get()));
+        notifyObservers(true);
+    }
+
     @Override
     public void onConfigurationChanged(Configuration configuration) {
         boolean isTablet = DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext);
-        if (mIsTablet == isTablet) {
+        if (mIsTablet == isTablet && !ChromeFeatureList.sToolbarTabletResizeRefactor.isEnabled()) {
             return;
         }
         mIsTablet = isTablet;
@@ -155,8 +173,15 @@
     @Override
     protected boolean shouldShowButton(@Nullable Tab tab) {
         if (tab == null) return false;
-        if (!super.shouldShowButton(tab) || mIsTablet) return false;
-
+        if (!super.shouldShowButton(tab)) return false;
+        // On tablets, the new tab button can be shown when the tab strip is not visible, if the
+        // tablet toolbar resize refactor is enabled.
+        if (mIsTablet) {
+            if (!ChromeFeatureList.sToolbarTabletResizeRefactor.isEnabled()
+                    || mTabStripVisibilitySupplier.get() != StripVisibilityState.HIDDEN_BY_FADE) {
+                return false;
+            }
+        }
         if (UrlUtilities.isNtpUrl(tab.getUrl())) return false;
 
         return true;
@@ -183,4 +208,16 @@
                         .setHighlightParams(params);
         return iphCommandBuilder;
     }
+
+    @Override
+    public void destroy() {
+        if (mTabStripVisibilitySupplier != null) {
+            mTabStripVisibilitySupplier.removeObserver(mOnTabStripVisibilityStateChanged);
+        }
+        super.destroy();
+    }
+
+    void setIsTabletForTesting(boolean isTablet) {
+        mIsTablet = isTablet;
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerUnitTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerUnitTest.java
index 20b506d..bef08bd 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerUnitTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerUnitTest.java
@@ -5,9 +5,12 @@
 package org.chromium.chrome.browser.toolbar.adaptive;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -28,7 +31,9 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
+import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -36,6 +41,9 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
+import org.chromium.chrome.browser.toolbar.optional_button.ButtonData;
+import org.chromium.chrome.browser.toolbar.optional_button.ButtonDataProvider;
+import org.chromium.chrome.browser.toolbar.top.tab_strip.StripVisibilityState;
 import org.chromium.chrome.browser.user_education.IphCommandBuilder;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.feature_engagement.FeatureConstants;
@@ -60,6 +68,10 @@
     @Mock TabCreator mTabCreator;
     @Mock private Supplier<Tab> mTabSupplier;
     @Mock private Tracker mTracker;
+    @Mock private ButtonDataProvider.ButtonDataObserver mButtonDataObserver;
+
+    private final ObservableSupplierImpl<@StripVisibilityState Integer>
+            mTabStripVisibilitySupplier = new ObservableSupplierImpl<>();
 
     private final Configuration mConfiguration = new Configuration();
     private OptionalNewTabButtonController mOptionalNewTabButtonController;
@@ -84,7 +96,8 @@
                         mActivityLifecycleDispatcher,
                         () -> mTabCreatorManager,
                         mTabSupplier,
-                        () -> mTracker);
+                        () -> mTracker,
+                        mTabStripVisibilitySupplier);
 
         TrackerFactory.setTrackerForTests(mTracker);
     }
@@ -130,4 +143,51 @@
         verify(mTracker, times(1))
                 .notifyEvent(EventConstants.ADAPTIVE_TOOLBAR_CUSTOMIZATION_NEW_TAB_OPENED);
     }
+
+    @Test
+    public void testShouldShowButton_nullTab() {
+        assertFalse(mOptionalNewTabButtonController.shouldShowButton(null));
+    }
+
+    @Test
+    @Config(qualifiers = "sw600dp")
+    @Features.DisableFeatures(ChromeFeatureList.TOOLBAR_TABLET_RESIZE_REFACTOR)
+    public void testShouldShowButton_tablet_resizeRefactorDisabled() {
+        mTabStripVisibilitySupplier.set(StripVisibilityState.HIDDEN_BY_FADE);
+        assertFalse(mOptionalNewTabButtonController.shouldShowButton(mTab));
+    }
+
+    @Test
+    @Config(qualifiers = "sw600dp")
+    @Features.EnableFeatures(ChromeFeatureList.TOOLBAR_TABLET_RESIZE_REFACTOR)
+    public void testShouldShowButton_tablet_resizeRefactorEnabled() {
+        // Hide the tab strip.
+        mTabStripVisibilitySupplier.set(StripVisibilityState.HIDDEN_BY_FADE);
+        assertTrue(mOptionalNewTabButtonController.shouldShowButton(mTab));
+
+        // Show the tab strip.
+        mTabStripVisibilitySupplier.set(StripVisibilityState.VISIBLE);
+        assertFalse(mOptionalNewTabButtonController.shouldShowButton(mTab));
+    }
+
+    @Test
+    @Config(qualifiers = "sw600dp")
+    @Features.EnableFeatures(ChromeFeatureList.TOOLBAR_TABLET_RESIZE_REFACTOR)
+    public void testShouldShowButton_onTabStripVisibilityStateChanged() {
+        mOptionalNewTabButtonController.addObserver(mButtonDataObserver);
+
+        // Hide the tab strip.
+        mTabStripVisibilitySupplier.set(StripVisibilityState.HIDDEN_BY_FADE);
+        ButtonData buttonData = mOptionalNewTabButtonController.get(mTab);
+        assertTrue(buttonData.canShow());
+        verify(mButtonDataObserver).buttonDataChanged(eq(true));
+
+        Mockito.clearInvocations(mButtonDataObserver);
+
+        // Show the tab strip.
+        mTabStripVisibilitySupplier.set(StripVisibilityState.VISIBLE);
+        buttonData = mOptionalNewTabButtonController.get(mTab);
+        assertFalse(buttonData.canShow());
+        verify(mButtonDataObserver).buttonDataChanged(eq(true));
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/FadeTransitionHandler.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/FadeTransitionHandler.java
index 2fc09f6..3149d79 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/FadeTransitionHandler.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/FadeTransitionHandler.java
@@ -9,7 +9,6 @@
 import org.chromium.base.CallbackController;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.build.annotations.NullMarked;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.toolbar.top.tab_strip.TabStripTransitionCoordinator.TabStripTransitionDelegate;
 import org.chromium.ui.base.ViewUtils;
 
@@ -49,10 +48,6 @@
     }
 
     private void requestTransition(boolean forceFadeInStrip) {
-        if (!ChromeFeatureList.isEnabled(
-                ChromeFeatureList.TAB_STRIP_TRANSITION_IN_DESKTOP_WINDOW)) {
-            return;
-        }
         mTabStripTransitionDelegateSupplier.runSyncOrOnAvailable(
                 mCallbackController.makeCancelable(
                         delegate -> maybeUpdateTabStripVisibility(forceFadeInStrip)));
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/StripVisibilityState.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/StripVisibilityState.java
index 83a44973..3bdaeae 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/StripVisibilityState.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/StripVisibilityState.java
@@ -8,8 +8,10 @@
 
 import org.chromium.build.annotations.NullMarked;
 
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
  * A set of states that represent the visibility of the tab strip. These types are bit flags, so
@@ -25,6 +27,7 @@
     StripVisibilityState.HIDDEN_BY_FADE,
 })
 @NullMarked
+@Target({ElementType.TYPE_USE})
 public @interface StripVisibilityState {
     /** Strip is visible. */
     int VISIBLE = 0;
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/TabStripTransitionCoordinatorUnitTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/TabStripTransitionCoordinatorUnitTest.java
index 1508fdb..8bb155b 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/TabStripTransitionCoordinatorUnitTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/tab_strip/TabStripTransitionCoordinatorUnitTest.java
@@ -47,7 +47,6 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Features.DisableFeatures;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.cc.input.BrowserControlsState;
 import org.chromium.chrome.R;
@@ -74,7 +73,6 @@
 /** Unit test for {@link TabStripTransitionCoordinator}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(qualifiers = "w600dp-h800dp", shadows = ShadowLooper.class)
-@EnableFeatures(ChromeFeatureList.TAB_STRIP_TRANSITION_IN_DESKTOP_WINDOW)
 @DisableFeatures(ChromeFeatureList.TAB_STRIP_LAYOUT_OPTIMIZATION)
 public class TabStripTransitionCoordinatorUnitTest {
     private static final int TEST_TAB_STRIP_HEIGHT = 40;
diff --git a/chrome/browser/ui/ash/login/BUILD.gn b/chrome/browser/ui/ash/login/BUILD.gn
index 73fbd6e..993f3b2 100644
--- a/chrome/browser/ui/ash/login/BUILD.gn
+++ b/chrome/browser/ui/ash/login/BUILD.gn
@@ -120,6 +120,7 @@
     "//chrome/browser/ash/policy/handlers",
     "//chrome/browser/ash/policy/remote_commands/crd",
     "//chrome/browser/ash/system",
+    "//chrome/browser/certificate_provider",
     "//chrome/browser/lifetime:termination_notification",
     "//chrome/browser/media/webrtc",
     "//chrome/browser/safe_browsing",
diff --git a/chrome/browser/ui/ash/test_util.cc b/chrome/browser/ui/ash/test_util.cc
index bf9f9e3..fd40081 100644
--- a/chrome/browser/ui/ash/test_util.cc
+++ b/chrome/browser/ui/ash/test_util.cc
@@ -85,13 +85,8 @@
   }
 }
 
-bool ChromeOSBrowserUITest::IsSnapWindowSupported() {
-  return true;
-}
-
 void ChromeOSBrowserUITest::SnapWindow(aura::Window* window,
                                        ash::SnapPosition position) {
-  CHECK(IsSnapWindowSupported());
   ash::SplitViewTestApi().SnapWindow(window, position);
 }
 
@@ -99,12 +94,7 @@
   ash::PinWindow(window, trusted);
 }
 
-bool ChromeOSBrowserUITest::IsIsShelfVisibleSupported() {
-  return true;
-}
-
 bool ChromeOSBrowserUITest::IsShelfVisible() {
-  CHECK(IsIsShelfVisibleSupported());
   return ash::ShelfTestApi().IsVisible();
 }
 
diff --git a/chrome/browser/ui/ash/test_util.h b/chrome/browser/ui/ash/test_util.h
index ab29d77f..0a43aa8c 100644
--- a/chrome/browser/ui/ash/test_util.h
+++ b/chrome/browser/ui/ash/test_util.h
@@ -47,18 +47,12 @@
   static void EnterOverviewMode();
   static void ExitOverviewMode();
 
-  // Checks whether the `SnapWindow` method can be called.
-  static bool IsSnapWindowSupported();
-
   // Snaps the given window to the given part of the screen
   static void SnapWindow(aura::Window* window, ash::SnapPosition position);
 
   // Pins the given window. This is also known as locked fullscreen mode.
   static void PinWindow(aura::Window* window, bool trusted);
 
-  // Checks whether the `IsShelfVisible` method can be called.
-  static bool IsIsShelfVisibleSupported();
-
   // Checks whether the ChromeOS UI currently shows the shelf (on the primary
   // display).
   static bool IsShelfVisible();
diff --git a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
index 70f5d3b..56d34594 100644
--- a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
 #include "base/strings/strcat.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/autofill/ui/ui_util.h"
diff --git a/chrome/browser/ui/autofill/bubble_manager_impl.cc b/chrome/browser/ui/autofill/bubble_manager_impl.cc
index 0e822c3..6b09644 100644
--- a/chrome/browser/ui/autofill/bubble_manager_impl.cc
+++ b/chrome/browser/ui/autofill/bubble_manager_impl.cc
@@ -122,7 +122,8 @@
   return time_added < other.time_added;
 }
 
-BubbleManagerImpl::BubbleManagerImpl(tabs::TabInterface* tab) {
+BubbleManagerImpl::BubbleManagerImpl(tabs::TabInterface* tab)
+    : tab_interface_(tab) {
   tab_subscriptions_.push_back(tab->RegisterWillDeactivate(base::BindRepeating(
       &BubbleManagerImpl::TabWillEnterBackground, base::Unretained(this))));
   tab_subscriptions_.push_back(tab->RegisterDidActivate(base::BindRepeating(
@@ -134,6 +135,14 @@
 void BubbleManagerImpl::RequestShowController(
     BubbleControllerBase& controller_to_show,
     bool force_show) {
+  base::WeakPtr<BubbleControllerBase> controller_weak_ptr =
+      controller_to_show.GetBubbleControllerBaseWeakPtr();
+
+  if (!tab_interface_->IsActivated()) {
+    AddToPendingQueue(controller_weak_ptr);
+    return;
+  }
+
   if (force_show) {
     base::UmaHistogramEnumeration("Autofill.Bubble.RequestShow.ForceShow",
                                   controller_to_show.GetBubbleType());
@@ -141,8 +150,6 @@
 
   base::UmaHistogramEnumeration("Autofill.Bubble.RequestShow",
                                 controller_to_show.GetBubbleType());
-  base::WeakPtr<BubbleControllerBase> controller_weak_ptr =
-      controller_to_show.GetBubbleControllerBaseWeakPtr();
 
   base::AutoReset<bool> show_request_guard(&handling_show_request_, true);
 
diff --git a/chrome/browser/ui/autofill/bubble_manager_impl.h b/chrome/browser/ui/autofill/bubble_manager_impl.h
index 5deafb7..85bbb20 100644
--- a/chrome/browser/ui/autofill/bubble_manager_impl.h
+++ b/chrome/browser/ui/autofill/bubble_manager_impl.h
@@ -99,6 +99,7 @@
   bool handling_tab_will_enter_background_request_ = false;
 
   std::vector<base::CallbackListSubscription> tab_subscriptions_;
+  raw_ptr<tabs::TabInterface> tab_interface_;
 };
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/bubble_manager_impl_unittest.cc b/chrome/browser/ui/autofill/bubble_manager_impl_unittest.cc
index 177ba40..e3440da 100644
--- a/chrome/browser/ui/autofill/bubble_manager_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/bubble_manager_impl_unittest.cc
@@ -36,6 +36,7 @@
   content::WebContents* GetContents() const override { return web_contents_; }
   void Activate();
   void Deactivate();
+  bool IsActivated() const override { return is_activated_; }
 
  private:
   // Only created if a non-null profile is provided.
@@ -757,4 +758,25 @@
       1);
 }
 
+// Tests that if the web contents is deactivated, the show bubble request leads
+// to bubble getting added to the queue.
+TEST_F(BubbleManagerImplTest, TabDeactivated_ShowAddsToQueue) {
+  std::unique_ptr<MockBubbleController> address_controller =
+      CreateController(BubbleType::kSaveUpdateAddress);
+
+  // Simulate the tab becoming hidden.
+  tab_interface()->Deactivate();
+
+  bubble_manager().RequestShowController(*address_controller,
+                                         /*force_show=*/false);
+  EXPECT_FALSE(address_controller->IsShowingBubble());
+
+  EXPECT_CALL(*address_controller, ShowBubble());
+
+  // Simulate the tab becoming visible.
+  tab_interface()->Activate();
+
+  EXPECT_TRUE(address_controller->IsShowingBubble());
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 0940aac..77eb990 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -52,7 +52,6 @@
 #include "chrome/browser/sync/sync_service_factory.h"
 #include "chrome/browser/translate/chrome_translate_client.h"
 #include "chrome/browser/ui/autofill/address_bubbles_controller.h"
-#include "chrome/browser/ui/autofill/autofill_field_promo_controller_impl.h"
 #include "chrome/browser/ui/autofill/autofill_suggestion_controller.h"
 #include "chrome/browser/ui/autofill/edit_address_profile_dialog_controller_impl.h"
 #include "chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h"
@@ -64,7 +63,6 @@
 #include "chrome/browser/ui/hats/hats_service.h"
 #include "chrome/browser/ui/hats/hats_service_factory.h"
 #include "chrome/browser/ui/hats/survey_config.h"
-#include "chrome/browser/ui/page_info/page_info_dialog.h"
 #include "chrome/browser/ui/passwords/ui_utils.h"
 #include "chrome/browser/ui/plus_addresses/plus_address_creation_controller.h"
 #include "chrome/browser/ui/singleton_tabs.h"
@@ -172,12 +170,14 @@
 #include "components/strings/grit/components_strings.h"
 #else  // !BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/ui/autofill/autofill_ai/save_or_update_autofill_ai_data_controller.h"
+#include "chrome/browser/ui/autofill/autofill_field_promo_controller_impl.h"
 #include "chrome/browser/ui/autofill/delete_address_profile_dialog_controller_impl.h"
 #include "chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
+#include "chrome/browser/ui/page_info/page_info_dialog.h"
 #include "chrome/browser/ui/plus_addresses/plus_address_error_dialog.h"
 #include "chrome/browser/ui/plus_addresses/plus_address_menu_model.h"  // nogncheck
 #include "chrome/browser/ui/tabs/public/tab_features.h"  // nogncheck
@@ -1179,9 +1179,11 @@
 }
 
 void ChromeAutofillClient::HideAutofillFieldIph() {
+#if !BUILDFLAG(IS_ANDROID)
   if (autofill_field_promo_controller_) {
     autofill_field_promo_controller_->Hide();
   }
+#endif  // !BUILDFLAG(IS_ANDROID)
 }
 
 void ChromeAutofillClient::NotifyIphFeatureUsed(
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index 9b2863b..18d8820 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -17,7 +17,6 @@
 #include "base/scoped_observation.h"
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/autofill/autofill_field_promo_controller.h"
 #include "chrome/browser/ui/autofill/autofill_suggestion_controller.h"
 #include "chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h"
 #include "components/autofill/content/browser/autofill_log_router_factory.h"
@@ -45,6 +44,8 @@
 #if BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/ui/autofill/autofill_snackbar_controller_impl.h"
 #include "components/autofill/core/browser/integrators/fast_checkout/fast_checkout_client.h"
+#else  // BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/ui/autofill/autofill_field_promo_controller.h"
 #endif  // BUILDFLAG(IS_ANDROID)
 
 namespace optimization_guide {
@@ -248,10 +249,12 @@
           keep_popup_open_for_testing);
     }
   }
+#if !BUILDFLAG(IS_ANDROID)
   void SetAutofillFieldPromoTesting(
       std::unique_ptr<AutofillFieldPromoController> test_controller) {
     autofill_field_promo_controller_ = std::move(test_controller);
   }
+#endif  // !BUILDFLAG(IS_ANDROID)
 #if BUILDFLAG(IS_ANDROID)
   void SetAutofillSnackbarControllerImplForTesting(
       std::unique_ptr<AutofillSnackbarControllerImpl>
@@ -323,9 +326,10 @@
   std::unique_ptr<FastCheckoutClient> fast_checkout_client_;
   std::unique_ptr<AutofillSnackbarControllerImpl>
       autofill_snackbar_controller_impl_;
-#endif
+#else   // BUILDFLAG(IS_ANDROID)
   std::unique_ptr<AutofillFieldPromoController>
       autofill_field_promo_controller_;
+#endif  // BUILDFLAG(IS_ANDROID)
   // Test addresses used to allow developers to test their forms.
   std::vector<AutofillProfile> test_addresses_;
   const AutofillAblationStudy ablation_study_;
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client_unittest.cc b/chrome/browser/ui/autofill/chrome_autofill_client_unittest.cc
index bace91c1..c9c50cc 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client_unittest.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client_unittest.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/autofill/ui/ui_util.h"
 #include "chrome/browser/plus_addresses/plus_address_service_factory.h"
 #include "chrome/browser/ssl/chrome_security_state_tab_helper.h"
-#include "chrome/browser/ui/autofill/autofill_field_promo_controller.h"
 #include "chrome/browser/ui/autofill/edit_address_profile_dialog_controller_impl.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/hats/hats_service.h"
@@ -78,6 +77,7 @@
 #include "chrome/browser/ui/android/autofill/autofill_save_card_delegate_android.h"
 #include "components/autofill/core/browser/payments/autofill_save_card_ui_info.h"
 #else
+#include "chrome/browser/ui/autofill/autofill_field_promo_controller.h"
 #include "chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h"
 #include "chrome/browser/ui/hats/hats_service_factory.h"
 #include "chrome/browser/ui/hats/mock_hats_service.h"
@@ -135,6 +135,7 @@
 };
 #endif
 
+#if !BUILDFLAG(IS_ANDROID)
 class MockAutofillFieldPromoController : public AutofillFieldPromoController {
  public:
   ~MockAutofillFieldPromoController() override = default;
@@ -143,6 +144,7 @@
   MOCK_METHOD(bool, IsMaybeShowing, (), (const override));
   MOCK_METHOD(const base::Feature&, GetFeaturePromo, (), (const override));
 };
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // This test class is needed to make the constructor public.
 class TestChromeAutofillClient : public ChromeAutofillClient {
@@ -178,6 +180,7 @@
 #endif
   }
 
+#if !BUILDFLAG(IS_ANDROID)
   void SetUpIphForTesting(const base::Feature& feature_promo) {
     auto autofill_field_promo_controller =
         std::make_unique<MockAutofillFieldPromoController>();
@@ -189,10 +192,13 @@
     client()->SetAutofillFieldPromoTesting(
         std::move(autofill_field_promo_controller));
   }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
   void TearDown() override {
     // Avoid that the raw pointer becomes dangling.
+#if !BUILDFLAG(IS_ANDROID)
     autofill_field_promo_controller_ = nullptr;
+#endif  // !BUILDFLAG(IS_ANDROID)
     ChromeRenderViewHostTestHarness::TearDown();
   }
 
@@ -205,9 +211,11 @@
     return ContentAutofillDriver::GetForRenderFrameHost(rfh);
   }
 
+#if !BUILDFLAG(IS_ANDROID)
   MockAutofillFieldPromoController* autofill_field_promo_controller() {
     return autofill_field_promo_controller_;
   }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_ANDROID)
   // Helper function to set up mock accessibility helper for Android tests.
@@ -261,7 +269,9 @@
       {.disable_server_communication = true}};
   base::test::ScopedFeatureList scoped_feature_list_{
       plus_addresses::features::kPlusAddressesEnabled};
+#if !BUILDFLAG(IS_ANDROID)
   raw_ptr<MockAutofillFieldPromoController> autofill_field_promo_controller_;
+#endif  // !BUILDFLAG(IS_ANDROID)
 #if BUILDFLAG(IS_ANDROID)
   std::unique_ptr<MockAutofillAccessibilityHelper> mock_accessibility_helper_;
 #endif
@@ -662,6 +672,7 @@
       /*on_confirmation_closed_callback=*/std::nullopt);
 }
 
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeAutofillClientTest, AutofillFieldIPH_NotShownByPromoController) {
   SetUpIphForTesting(feature_engagement::kIPHAutofillAiOptInFeature);
 
@@ -714,6 +725,7 @@
 
   testing::Mock::VerifyAndClearExpectations(autofill_field_promo_controller());
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 class ChromeAutofillClientTestWithWindow : public BrowserWithTestWindowTest {
  public:
diff --git a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc
index 03c59be..f997b44 100644
--- a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc
@@ -272,6 +272,11 @@
 void MandatoryReauthBubbleControllerImpl::UpdatePageActionIcon() {
 // Page action icons do not exist for Android.
 #if !BUILDFLAG(IS_ANDROID)
+  // If WebContents is being destroyed, `TabFeatures` may have been already
+  // destroyed. This check prevents a UAF.
+  if (web_contents()->IsBeingDestroyed()) {
+    return;
+  }
   if (!IsPageActionMigrated(PageActionIconType::kMandatoryReauth)) {
     AutofillBubbleControllerBase::UpdatePageActionIcon();
   }
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 335192e..3c732ac 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -967,7 +967,8 @@
 
   if (disposition == WindowOpenDisposition::CURRENT_TAB ||
       disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB) {
-    extensions::MaybeShowExtensionControlledHomeNotification(browser);
+    extensions::MaybeShowExtensionControlledHomeNotification(
+        browser, browser->tab_strip_model()->GetActiveWebContents());
   }
 #endif
 
@@ -1164,11 +1165,6 @@
     return;
   }
 
-  if (!toast_features::IsEnabled(toast_features::kPinnedTabToastOnClose)) {
-    CloseSelectedTabAndRecordTabCountMetric(browser);
-    return;
-  }
-
   ToastController* toast_controller = browser->GetFeatures().toast_controller();
   if (!toast_controller) {
     CloseSelectedTabAndRecordTabCountMetric(browser);
diff --git a/chrome/browser/ui/browser_list.h b/chrome/browser/ui/browser_list.h
index 14fc653..05853001 100644
--- a/chrome/browser/ui/browser_list.h
+++ b/chrome/browser/ui/browser_list.h
@@ -43,22 +43,6 @@
   using const_iterator = BrowserVector::const_iterator;
   using const_reverse_iterator = BrowserVector::const_reverse_iterator;
 
-  struct BrowsersOrderedByActivationRange {
-    const raw_ref<const BrowserList> browser_list;
-
-    const_reverse_iterator begin() const {
-      return browser_list->begin_browsers_ordered_by_activation();
-    }
-    const_reverse_iterator end() const {
-      return browser_list->end_browsers_ordered_by_activation();
-    }
-
-   private:
-    // Stack allocated only to reduce risk of out of bounds lifetime with
-    // |browser_list|.
-    STACK_ALLOCATED();
-  };
-
   BrowserList(const BrowserList&) = delete;
   BrowserList& operator=(const BrowserList&) = delete;
 
diff --git a/chrome/browser/ui/browser_window/internal/BUILD.gn b/chrome/browser/ui/browser_window/internal/BUILD.gn
index 204d98c..f9848ed 100644
--- a/chrome/browser/ui/browser_window/internal/BUILD.gn
+++ b/chrome/browser/ui/browser_window/internal/BUILD.gn
@@ -55,6 +55,7 @@
       "//chrome/browser/ui/find_bar",
       "//chrome/browser/ui/lens",
       "//chrome/browser/ui/performance_controls",
+      "//chrome/browser/ui/promos:controller",
       "//chrome/browser/ui/signin",
       "//chrome/browser/ui/sync",
       "//chrome/browser/ui/tabs:glic",
diff --git a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskImpl.java b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskImpl.java
index 871c2ff..433b2182 100644
--- a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskImpl.java
+++ b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskImpl.java
@@ -7,6 +7,7 @@
 import static org.chromium.build.NullUtil.assertNonNull;
 import static org.chromium.build.NullUtil.assumeNonNull;
 
+import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.content.Context;
@@ -133,6 +134,8 @@
 
     private @Nullable AndroidBrowserWindowCreateParams mCreateParams;
 
+    private boolean mShouldDispatchPendingDeactivate;
+
     /**
      * Listener for window insets animation.
      *
@@ -370,10 +373,14 @@
     @Override
     public Rect getRestoredBoundsInDp() {
         if (mState.get() == State.PENDING) {
+            var initialBounds = assumeNonNull(mCreateParams).getInitialBounds();
             if (mPendingActionManager.isActionRequested(PendingAction.SET_BOUNDS)) {
                 return assertNonNull(mPendingActionManager.getPendingBoundsInDp());
+            } else if (mPendingActionManager.isActionRequested(PendingAction.RESTORE)) {
+                var pendingRestoredBounds = mPendingActionManager.getPendingRestoredBoundsInDp();
+                return pendingRestoredBounds == null ? initialBounds : pendingRestoredBounds;
             }
-            return assumeNonNull(mCreateParams).getInitialBounds();
+            return initialBounds;
         }
 
         synchronized (mActivityWindowAndroidLock) {
@@ -432,18 +439,7 @@
         }
 
         synchronized (mActivityWindowAndroidLock) {
-            var activityWindowAndroid =
-                    getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
-            var activity =
-                    activityWindowAndroid != null
-                            ? activityWindowAndroid.getActivity().get()
-                            : null;
-            if (activity == null) return;
-            // Activate the Task if it's already visible.
-            // TODO(http://crbug.com/424860292): create a new window when task is invisible.
-            if (isVisibleInternalLocked(activity)) {
-                activateInternalLocked(activity);
-            }
+            showInternalLocked();
         }
     }
 
@@ -514,29 +510,19 @@
         }
 
         synchronized (mActivityWindowAndroidLock) {
-            var activityWindowAndroid =
-                    getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
-            if (activityWindowAndroid == null) return;
-            Activity activity = activityWindowAndroid.getActivity().get();
-            if (activity == null) return;
-            activity.finishAndRemoveTask();
+            closeInternalLocked();
         }
     }
 
     @Override
     public void activate() {
-        synchronized (mActivityWindowAndroidLock) {
-            if (mState.get() == State.PENDING) {
-                mPendingActionManager.requestAction(PendingAction.ACTIVATE);
-                return;
-            }
+        if (mState.get() == State.PENDING) {
+            mPendingActionManager.requestAction(PendingAction.ACTIVATE);
+            return;
+        }
 
-            var activityWindowAndroid =
-                    getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
-            if (activityWindowAndroid == null) return;
-            Activity activity = activityWindowAndroid.getActivity().get();
-            if (activity == null) return;
-            activateInternalLocked(activity);
+        synchronized (mActivityWindowAndroidLock) {
+            activateInternalLocked();
         }
     }
 
@@ -564,18 +550,7 @@
         }
 
         synchronized (mActivityWindowAndroidLock) {
-            var activityWindowAndroid =
-                    getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
-            if (activityWindowAndroid == null) return;
-            Activity activity = activityWindowAndroid.getActivity().get();
-            if (activity == null) return;
-            // No maximize action in non desktop window mode.
-            if (!activity.isInMultiWindowMode()) return;
-            if (isRestoredInternalLocked(activityWindowAndroid)) {
-                mRestoredBoundsInPx = getCurrentBoundsInPxLocked(activityWindowAndroid);
-            }
-            Rect maximizedBounds = getMaxBoundsInPx(activity.getWindowManager());
-            setBoundsInPxLocked(activity, activityWindowAndroid.getDisplay(), maximizedBounds);
+            maximizeInternalLocked();
         }
     }
 
@@ -592,17 +567,7 @@
         }
 
         synchronized (mActivityWindowAndroidLock) {
-            var activityWindowAndroid =
-                    getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
-            // https://crbug.com/445247646: minimize an app which is already minimized might make
-            // app unable to be activated again.
-            if (activityWindowAndroid == null || isMinimizedInternalLocked(activityWindowAndroid)) {
-                return;
-            }
-            if (isRestoredInternalLocked(activityWindowAndroid)) {
-                mRestoredBoundsInPx = getCurrentBoundsInPxLocked(activityWindowAndroid);
-            }
-            getActivity(activityWindowAndroid).moveTaskToBack(/* nonRoot= */ true);
+            minimizeInternalLocked();
         }
     }
 
@@ -614,33 +579,21 @@
         }
 
         synchronized (mActivityWindowAndroidLock) {
-            var activityWindowAndroid =
-                    getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
-            if (activityWindowAndroid == null) return;
-            Activity activity = activityWindowAndroid.getActivity().get();
-            if (activity == null || mRestoredBoundsInPx == null) return;
-            setBoundsInPxLocked(activity, activityWindowAndroid.getDisplay(), mRestoredBoundsInPx);
+            restoreInternalLocked();
         }
     }
 
     @Override
     public void setBoundsInDp(Rect boundsInDp) {
         if (mState.get() == State.PENDING) {
-            mPendingActionManager.requestSetBounds(boundsInDp);
+            if (!boundsInDp.isEmpty()) {
+                mPendingActionManager.requestSetBounds(boundsInDp);
+            }
             return;
         }
 
         synchronized (mActivityWindowAndroidLock) {
-            var activityWindowAndroid =
-                    getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
-            if (activityWindowAndroid == null) return;
-            Activity activity = activityWindowAndroid.getActivity().get();
-            if (activity == null) return;
-
-            Rect boundsInPx =
-                    DisplayUtil.scaleToEnclosingRect(
-                            boundsInDp, activityWindowAndroid.getDisplay().getDipScale());
-            setBoundsInPxLocked(activity, activityWindowAndroid.getDisplay(), boundsInPx);
+            setBoundsInDpInternalLocked(boundsInDp);
         }
     }
 
@@ -648,6 +601,10 @@
     public void onTopResumedActivityChangedWithNative(boolean isTopResumedActivity) {
         if (isTopResumedActivity) {
             mLastActivatedTimeMillis.set(TimeUtils.elapsedRealtimeMillis());
+            if (mShouldDispatchPendingDeactivate) {
+                ChromeAndroidTaskTrackerImpl.getInstance().activatePenultimatelyActivatedTask();
+                mShouldDispatchPendingDeactivate = false;
+            }
         }
 
         synchronized (mFeaturesLock) {
@@ -746,7 +703,63 @@
                 mId = getActivity(activityWindowAndroid).getTaskId();
                 mPendingId = null;
                 mState.set(State.ALIVE);
-                // TODO (crbug.com/444745184): Dispatch pending actions.
+                dispatchPendingActionsLocked(activityWindowAndroid);
+            }
+        }
+    }
+
+    @GuardedBy("mActivityWindowAndroidLock")
+    @SuppressLint("NewApi")
+    private void dispatchPendingActionsLocked(ActivityWindowAndroid activityWindowAndroid) {
+        // Initiate actions on a live Task.
+        assertAlive();
+        Rect boundsInDp = mPendingActionManager.getPendingBoundsInDp();
+        Rect restoredBoundsInDp = mPendingActionManager.getPendingRestoredBoundsInDp();
+        @PendingAction int[] pendingActions = mPendingActionManager.getAndClearPendingActions();
+        for (@PendingAction int action : pendingActions) {
+            if (action == PendingAction.NONE) continue;
+            switch (action) {
+                case PendingAction.SHOW:
+                    showInternalLocked();
+                    break;
+                case PendingAction.SHOW_INACTIVE:
+                case PendingAction.DEACTIVATE:
+                    // We will not activate the penultimately active task just yet (in order to
+                    // deactivate the current task) because at the time this method is invoked, the
+                    // current task's activated time is not guaranteed to be set in order to
+                    // correctly determine the penultimate task. We will therefore dispatch this
+                    // action after the current task's activated time is set.
+                    mShouldDispatchPendingDeactivate = true;
+                    break;
+                case PendingAction.CLOSE:
+                    closeInternalLocked();
+                    break;
+                case PendingAction.ACTIVATE:
+                    activateInternalLocked();
+                    break;
+                case PendingAction.MAXIMIZE:
+                    maximizeInternalLocked();
+                    break;
+                case PendingAction.MINIMIZE:
+                    minimizeInternalLocked();
+                    break;
+                case PendingAction.RESTORE:
+                    // RESTORE should be ignored to fall back to default startup bounds if
+                    // non-empty, non-default bounds are not requested in pending state.
+                    if (restoredBoundsInDp != null && !restoredBoundsInDp.isEmpty()) {
+                        mRestoredBoundsInPx =
+                                DisplayUtil.scaleToEnclosingRect(
+                                        restoredBoundsInDp,
+                                        activityWindowAndroid.getDisplay().getDipScale());
+                        restoreInternalLocked();
+                    }
+                    break;
+                case PendingAction.SET_BOUNDS:
+                    assert boundsInDp != null;
+                    setBoundsInDpInternalLocked(boundsInDp);
+                    break;
+                default:
+                    assert false : "Unsupported pending action.";
             }
         }
     }
@@ -904,12 +917,93 @@
     }
 
     @GuardedBy("mActivityWindowAndroidLock")
-    private void activateInternalLocked(Activity activity) {
+    private void showInternalLocked() {
+        var activityWindowAndroid = getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
+        var activity =
+                activityWindowAndroid != null ? activityWindowAndroid.getActivity().get() : null;
+        if (activity == null) return;
+        // Activate the Task if it's already visible.
+        if (isVisibleInternalLocked(activity)) {
+            ActivityManager activityManager =
+                    (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
+            activityManager.moveTaskToFront(activity.getTaskId(), 0);
+        }
+    }
+
+    @GuardedBy("mActivityWindowAndroidLock")
+    private void closeInternalLocked() {
+        var activityWindowAndroid = getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
+        if (activityWindowAndroid == null) return;
+        Activity activity = activityWindowAndroid.getActivity().get();
+        if (activity == null) return;
+        activity.finishAndRemoveTask();
+    }
+
+    @GuardedBy("mActivityWindowAndroidLock")
+    private void activateInternalLocked() {
+        var activityWindowAndroid = getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
+        if (activityWindowAndroid == null) return;
+        Activity activity = activityWindowAndroid.getActivity().get();
+        if (activity == null) return;
+
         ActivityManager activityManager =
                 (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
         activityManager.moveTaskToFront(activity.getTaskId(), 0);
     }
 
+    @GuardedBy("mActivityWindowAndroidLock")
+    @RequiresApi(api = VERSION_CODES.R)
+    private void maximizeInternalLocked() {
+        var activityWindowAndroid = getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
+        if (activityWindowAndroid == null) return;
+        Activity activity = activityWindowAndroid.getActivity().get();
+        if (activity == null) return;
+        // No maximize action in non desktop window mode.
+        if (!activity.isInMultiWindowMode()) return;
+        if (isRestoredInternalLocked(activityWindowAndroid)) {
+            mRestoredBoundsInPx = getCurrentBoundsInPxLocked(activityWindowAndroid);
+        }
+        Rect maximizedBounds = getMaxBoundsInPx(activity.getWindowManager());
+        setBoundsInPxLocked(activity, activityWindowAndroid.getDisplay(), maximizedBounds);
+    }
+
+    @GuardedBy("mActivityWindowAndroidLock")
+    @RequiresApi(api = VERSION_CODES.R)
+    private void minimizeInternalLocked() {
+        var activityWindowAndroid = getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
+        // https://crbug.com/445247646: minimize an app which is already minimized might make
+        // app unable to be activated again.
+        if (activityWindowAndroid == null || isMinimizedInternalLocked(activityWindowAndroid)) {
+            return;
+        }
+        if (isRestoredInternalLocked(activityWindowAndroid)) {
+            mRestoredBoundsInPx = getCurrentBoundsInPxLocked(activityWindowAndroid);
+        }
+        getActivity(activityWindowAndroid).moveTaskToBack(/* nonRoot= */ true);
+    }
+
+    @GuardedBy("mActivityWindowAndroidLock")
+    private void restoreInternalLocked() {
+        var activityWindowAndroid = getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
+        if (activityWindowAndroid == null) return;
+        Activity activity = activityWindowAndroid.getActivity().get();
+        if (activity == null || mRestoredBoundsInPx == null) return;
+        setBoundsInPxLocked(activity, activityWindowAndroid.getDisplay(), mRestoredBoundsInPx);
+    }
+
+    @GuardedBy("mActivityWindowAndroidLock")
+    private void setBoundsInDpInternalLocked(Rect boundsInDp) {
+        var activityWindowAndroid = getActivityWindowAndroidInternalLocked(/* assertAlive= */ true);
+        if (activityWindowAndroid == null) return;
+        Activity activity = activityWindowAndroid.getActivity().get();
+        if (activity == null) return;
+
+        Rect boundsInPx =
+                DisplayUtil.scaleToEnclosingRect(
+                        boundsInDp, activityWindowAndroid.getDisplay().getDipScale());
+        setBoundsInPxLocked(activity, activityWindowAndroid.getDisplay(), boundsInPx);
+    }
+
     @RequiresApi(api = VERSION_CODES.R)
     private static Rect getMaxBoundsInPx(WindowManager windowManager) {
         var insets =
diff --git a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskImplUnitTest.java b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskImplUnitTest.java
index 511a150..8f63b97 100644
--- a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskImplUnitTest.java
+++ b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskImplUnitTest.java
@@ -23,6 +23,8 @@
 import static org.chromium.build.NullUtil.assertNonNull;
 
 import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.graphics.Rect;
@@ -30,6 +32,7 @@
 import android.view.Window;
 import android.view.WindowInsets;
 import android.view.WindowInsetsController;
+import android.view.WindowManager;
 import android.view.WindowMetrics;
 
 import androidx.core.view.WindowInsetsControllerCompat;
@@ -753,16 +756,7 @@
         when(mockActivity.isInMultiWindowMode()).thenReturn(true);
 
         // Mock getMaximizedBounds().
-        var mockMaxWindowMetrics = mock(WindowMetrics.class);
-        var mockMaxWindowInsets = mock(WindowInsets.class);
-        when(mockWindowManager.getMaximumWindowMetrics()).thenReturn(mockMaxWindowMetrics);
-        when(mockMaxWindowMetrics.getWindowInsets()).thenReturn(mockMaxWindowInsets);
-        var tappableInsets = Insets.of(0, 10, 0, 20);
-        when(mockMaxWindowInsets.getInsets(WindowInsets.Type.tappableElement()))
-                .thenReturn(tappableInsets);
-        var fullscreenBounds = new Rect(0, 0, 1920, 1080);
-        when(mockMaxWindowMetrics.getBounds()).thenReturn(fullscreenBounds);
-        var maximizedBounds = new Rect(0, 10, 1920, 1060);
+        var maximizedBounds = mockMaximizedBounds(mockWindowManager);
 
         // Act.
         chromeAndroidTask.maximize();
@@ -785,10 +779,6 @@
                         /* taskId= */ 1);
         var chromeAndroidTask =
                 (ChromeAndroidTaskImpl) chromeAndroidTaskWithMockDeps.mChromeAndroidTask;
-        var mockActivityWindowAndroid =
-                chromeAndroidTaskWithMockDeps
-                        .mActivityWindowAndroidMocks
-                        .mMockActivityWindowAndroid;
         var mockWindowManager =
                 chromeAndroidTaskWithMockDeps.mActivityWindowAndroidMocks.mMockWindowManager;
 
@@ -804,15 +794,7 @@
         when(mockWindowMetrics.getBounds()).thenReturn(currentBounds);
 
         // Mock getMaximizedBounds().
-        var mockMaxWindowMetrics = mock(WindowMetrics.class);
-        var mockMaxWindowInsets = mock(WindowInsets.class);
-        when(mockWindowManager.getMaximumWindowMetrics()).thenReturn(mockMaxWindowMetrics);
-        when(mockMaxWindowMetrics.getWindowInsets()).thenReturn(mockMaxWindowInsets);
-        var tappableInsets = Insets.of(0, 10, 0, 20);
-        when(mockMaxWindowInsets.getInsets(WindowInsets.Type.tappableElement()))
-                .thenReturn(tappableInsets);
-        var fullscreenBounds = new Rect(0, 0, 1920, 1080);
-        when(mockMaxWindowMetrics.getBounds()).thenReturn(fullscreenBounds);
+        mockMaximizedBounds(mockWindowManager);
 
         // Act & Assert.
         assertFalse(chromeAndroidTask.isMaximized());
@@ -1173,7 +1155,7 @@
     }
 
     @Test
-    public void setBounds_whenPending_enqueuesPendingAction() {
+    public void setBounds_whenPending_nonEmptyBounds_enqueuesPendingAction() {
         // Arrange.
         var mockParams =
                 ChromeAndroidTaskUnitTestSupport.createMockAndroidBrowserWindowCreateParams();
@@ -1191,6 +1173,22 @@
     }
 
     @Test
+    public void setBounds_whenPending_emptyBounds_ignoresPendingAction() {
+        // Arrange.
+        var mockParams =
+                ChromeAndroidTaskUnitTestSupport.createMockAndroidBrowserWindowCreateParams();
+        var task = new ChromeAndroidTaskImpl(/* pendingId= */ 1, mockParams);
+
+        // Act.
+        task.setBoundsInDp(new Rect());
+
+        // Assert.
+        var pendingActionManager = task.getPendingActionManagerForTesting();
+        assertEquals(PendingAction.NONE, pendingActionManager.getPendingActionsForTesting()[0]);
+        assertNull(pendingActionManager.getPendingBoundsInDp());
+    }
+
+    @Test
     public void isActive_whenPending_withNoPendingShowOrActivate_returnsFalse() {
         // Arrange.
         var mockParams =
@@ -1317,7 +1315,7 @@
     }
 
     @Test
-    public void getRestoredBoundsInDp_whenPending_withPendingSetBounds_returnsPendingBounds() {
+    public void getRestoredBoundsInDp_whenPending_withNonEmptyBounds_returnsPendingBounds() {
         // Arrange.
         var mockParams =
                 ChromeAndroidTaskUnitTestSupport.createMockAndroidBrowserWindowCreateParams();
@@ -1332,6 +1330,39 @@
 
     @Test
     public void
+            getRestoredBoundsInDp_whenPending_withNonEmptyRestoredBounds_returnsPendingRestoredBounds() {
+        // Arrange.
+        var mockParams =
+                ChromeAndroidTaskUnitTestSupport.createMockAndroidBrowserWindowCreateParams();
+        var task = new ChromeAndroidTaskImpl(/* pendingId= */ 1, mockParams);
+        // Request SET_BOUNDS, MAXIMIZE, and RESTORE in pending state.
+        var bounds = new Rect(100, 100, 600, 800);
+        task.setBoundsInDp(bounds);
+        task.maximize();
+        task.restore();
+
+        // Act and Assert.
+        assertEquals(bounds, task.getRestoredBoundsInDp());
+    }
+
+    @Test
+    public void
+            getRestoredBoundsInDp_whenPending_withoutPendingRestoredBounds_returnsInitialBounds() {
+        // Arrange.
+        var bounds = new Rect(100, 100, 600, 800);
+        var mockParams =
+                ChromeAndroidTaskUnitTestSupport.createMockAndroidBrowserWindowCreateParams(
+                        BrowserWindowType.NORMAL, bounds, WindowShowState.DEFAULT);
+        var task = new ChromeAndroidTaskImpl(/* pendingId= */ 1, mockParams);
+        // Request RESTORE in pending state.
+        task.restore();
+
+        // Act and Assert.
+        assertEquals(bounds, task.getRestoredBoundsInDp());
+    }
+
+    @Test
+    public void
             getRestoredBoundsInDp_whenPending_withNonEmptyInitialBoundsInCreateParams_returnsInitialBounds() {
         // Arrange.
         var bounds = new Rect(100, 100, 600, 800);
@@ -1418,4 +1449,256 @@
         // Act and Assert.
         assertTrue(task.isVisible());
     }
+
+    @Test
+    public void setActivityWindowAndroid_fromPendingState_dispatchesPendingShow() {
+        // Arrange: Create pending task.
+        var mockParams =
+                ChromeAndroidTaskUnitTestSupport.createMockAndroidBrowserWindowCreateParams();
+        var task = new ChromeAndroidTaskImpl(/* pendingId= */ 1, mockParams);
+        // Arrange: Request SHOW on a pending task.
+        task.show();
+        int taskId = 2;
+        // Arrange: Setup WindowAndroid.
+        var activityWindowAndroid =
+                ChromeAndroidTaskUnitTestSupport.createMockActivityWindowAndroid(taskId);
+        var mockActivity = activityWindowAndroid.getActivity().get();
+        var mockActivityManager =
+                (ActivityManager) mockActivity.getSystemService(Context.ACTIVITY_SERVICE);
+        ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.CREATED);
+        ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.RESUMED);
+
+        // Act.
+        task.setActivityWindowAndroid(activityWindowAndroid, mock(TabModel.class));
+
+        // Assert.
+        verify(mockActivityManager).moveTaskToFront(taskId, 0);
+    }
+
+    @Test
+    public void setActivityWindowAndroid_fromPendingState_dispatchesPendingClose() {
+        // Arrange: Create pending task.
+        var mockParams =
+                ChromeAndroidTaskUnitTestSupport.createMockAndroidBrowserWindowCreateParams();
+        var task = new ChromeAndroidTaskImpl(/* pendingId= */ 1, mockParams);
+        // Arrange: Request CLOSE on a pending task.
+        task.close();
+        int taskId = 2;
+        // Arrange: Setup WindowAndroid.
+        var activityWindowAndroid =
+                ChromeAndroidTaskUnitTestSupport.createMockActivityWindowAndroid(taskId);
+        var mockActivity = activityWindowAndroid.getActivity().get();
+
+        // Act.
+        task.setActivityWindowAndroid(activityWindowAndroid, mock(TabModel.class));
+
+        // Assert.
+        verify(mockActivity).finishAndRemoveTask();
+    }
+
+    @Test
+    public void setActivityWindowAndroid_fromPendingState_dispatchesPendingActivate() {
+        // Arrange: Create pending task.
+        var mockParams =
+                ChromeAndroidTaskUnitTestSupport.createMockAndroidBrowserWindowCreateParams();
+        var task = new ChromeAndroidTaskImpl(/* pendingId= */ 1, mockParams);
+        // Arrange: Request ACTIVATE on a pending task.
+        task.activate();
+        // Arrange: Setup WindowAndroid.
+        int taskId = 2;
+        var activityWindowAndroid =
+                ChromeAndroidTaskUnitTestSupport.createMockActivityWindowAndroid(taskId);
+        var mockActivity = activityWindowAndroid.getActivity().get();
+        var mockActivityManager =
+                (ActivityManager) mockActivity.getSystemService(Context.ACTIVITY_SERVICE);
+
+        // Act.
+        task.setActivityWindowAndroid(activityWindowAndroid, mock(TabModel.class));
+
+        // Assert.
+        verify(mockActivityManager).moveTaskToFront(taskId, 0);
+    }
+
+    @Test
+    @Config(sdk = Build.VERSION_CODES.BAKLAVA)
+    @SuppressLint("NewApi" /* @Config already specifies the required SDK */)
+    public void setActivityWindowAndroid_fromPendingState_dispatchesPendingMaximize() {
+        // Arrange: Create pending task.
+        var chromeAndroidTaskWithMockDeps =
+                ChromeAndroidTaskUnitTestSupport.createChromeAndroidTaskWithMockDeps(
+                        /* taskId= */ 1, /* mockNatives= */ true, /* isPendingTask= */ true);
+        var apiDelegate = chromeAndroidTaskWithMockDeps.mMockAconfigFlaggedApiDelegate;
+        var chromeAndroidTask = chromeAndroidTaskWithMockDeps.mChromeAndroidTask;
+        // Arrange: Request MAXIMIZE on a pending task.
+        chromeAndroidTask.maximize();
+        // Arrange: Setup WindowAndroid and mock maximized bounds.
+        var activityWindowAndroid =
+                chromeAndroidTaskWithMockDeps
+                        .mActivityWindowAndroidMocks
+                        .mMockActivityWindowAndroid;
+        var mockActivity = activityWindowAndroid.getActivity().get();
+        when(mockActivity.isInMultiWindowMode()).thenReturn(true);
+        var maximizedBounds =
+                mockMaximizedBounds(
+                        chromeAndroidTaskWithMockDeps
+                                .mActivityWindowAndroidMocks
+                                .mMockWindowManager);
+
+        // Act.
+        chromeAndroidTask.setActivityWindowAndroid(
+                activityWindowAndroid, chromeAndroidTaskWithMockDeps.mMockTabModel);
+
+        // Assert.
+        var boundsCaptor = ArgumentCaptor.forClass(Rect.class);
+        verify(apiDelegate).moveTaskTo(any(), anyInt(), boundsCaptor.capture());
+        var capturedBounds = boundsCaptor.getValue();
+        assertEquals(maximizedBounds, capturedBounds);
+    }
+
+    @Test
+    @Config(sdk = Build.VERSION_CODES.R)
+    public void setActivityWindowAndroid_fromPendingState_dispatchesPendingMinimize() {
+        // Arrange: Create pending task.
+        var chromeAndroidTaskWithMockDeps =
+                ChromeAndroidTaskUnitTestSupport.createChromeAndroidTaskWithMockDeps(
+                        /* taskId= */ 1, /* mockNatives= */ true, /* isPendingTask= */ true);
+        var chromeAndroidTask = chromeAndroidTaskWithMockDeps.mChromeAndroidTask;
+        // Arrange: Request MINIMIZE on a pending task.
+        chromeAndroidTask.minimize();
+        // Arrange: Setup WindowAndroid.
+        int taskId = 2;
+        var activityWindowAndroid =
+                ChromeAndroidTaskUnitTestSupport.createMockActivityWindowAndroid(taskId);
+        var mockActivity = activityWindowAndroid.getActivity().get();
+        ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.CREATED);
+        ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.RESUMED);
+
+        // Act.
+        chromeAndroidTask.setActivityWindowAndroid(
+                activityWindowAndroid, chromeAndroidTaskWithMockDeps.mMockTabModel);
+
+        // Assert.
+        verify(mockActivity).moveTaskToBack(true);
+    }
+
+    @Test
+    @Config(sdk = Build.VERSION_CODES.BAKLAVA)
+    @SuppressLint("NewApi" /* @Config already specifies the required SDK */)
+    public void
+            setActivityWindowAndroid_fromPendingState_withNonEmptyPendingBounds_dispatchesPendingRestore() {
+        // Arrange: Create pending task.
+        var chromeAndroidTaskWithMockDeps =
+                ChromeAndroidTaskUnitTestSupport.createChromeAndroidTaskWithMockDeps(
+                        /* taskId= */ 1, /* mockNatives= */ true, /* isPendingTask= */ true);
+        var apiDelegate = chromeAndroidTaskWithMockDeps.mMockAconfigFlaggedApiDelegate;
+        var chromeAndroidTask =
+                (ChromeAndroidTaskImpl) chromeAndroidTaskWithMockDeps.mChromeAndroidTask;
+        // Arrange: Setup display parameters.
+        var displayAndroid =
+                chromeAndroidTaskWithMockDeps.mActivityWindowAndroidMocks.mMockDisplayAndroid;
+        float dipScale = 2.0f;
+        when(displayAndroid.getDipScale()).thenReturn(dipScale);
+        // Arrange: Sequentially request SET_BOUNDS, MAXIMIZE, and RESTORE on a pending task.
+        Rect pendingBoundsInDp = new Rect(10, 20, 800, 600);
+        chromeAndroidTask.setBoundsInDp(pendingBoundsInDp);
+        chromeAndroidTask.maximize();
+        chromeAndroidTask.restore();
+        // Arrange: Setup WindowAndroid.
+        var activityWindowAndroid =
+                chromeAndroidTaskWithMockDeps
+                        .mActivityWindowAndroidMocks
+                        .mMockActivityWindowAndroid;
+
+        // Act.
+        chromeAndroidTask.setActivityWindowAndroid(
+                activityWindowAndroid, chromeAndroidTaskWithMockDeps.mMockTabModel);
+
+        // Assert.
+        Rect expectedBoundsInPx = DisplayUtil.scaleToEnclosingRect(pendingBoundsInDp, dipScale);
+        var boundsCaptor = ArgumentCaptor.forClass(Rect.class);
+        verify(apiDelegate).moveTaskTo(any(), anyInt(), boundsCaptor.capture());
+        assertEquals(expectedBoundsInPx, boundsCaptor.getValue());
+    }
+
+    @Test
+    @Config(sdk = Build.VERSION_CODES.BAKLAVA)
+    @SuppressLint("NewApi" /* @Config already specifies the required SDK */)
+    public void
+            setActivityWindowAndroid_fromPendingState_withEmptyPendingBounds_ignoresPendingRestore() {
+        // Arrange: Create pending task.
+        var chromeAndroidTaskWithMockDeps =
+                ChromeAndroidTaskUnitTestSupport.createChromeAndroidTaskWithMockDeps(
+                        /* taskId= */ 1, /* mockNatives= */ true, /* isPendingTask= */ true);
+        var apiDelegate = chromeAndroidTaskWithMockDeps.mMockAconfigFlaggedApiDelegate;
+        var chromeAndroidTask =
+                (ChromeAndroidTaskImpl) chromeAndroidTaskWithMockDeps.mChromeAndroidTask;
+        // Arrange: Request RESTORE on a pending task.
+        chromeAndroidTask.restore();
+        // Arrange: Setup WindowAndroid.
+        var activityWindowAndroid =
+                chromeAndroidTaskWithMockDeps
+                        .mActivityWindowAndroidMocks
+                        .mMockActivityWindowAndroid;
+
+        // Act.
+        chromeAndroidTask.setActivityWindowAndroid(
+                activityWindowAndroid, chromeAndroidTaskWithMockDeps.mMockTabModel);
+
+        // Assert.
+        verify(apiDelegate, never()).moveTaskTo(any(), anyInt(), any());
+    }
+
+    @Test
+    @Config(sdk = Build.VERSION_CODES.BAKLAVA)
+    @SuppressLint("NewApi" /* @Config already specifies the required SDK */)
+    public void setActivityWindowAndroid_fromPendingState_dispatchesPendingSetBounds() {
+        // Arrange: Create pending task.
+        var chromeAndroidTaskWithMockDeps =
+                ChromeAndroidTaskUnitTestSupport.createChromeAndroidTaskWithMockDeps(
+                        /* taskId= */ 1, /* mockNatives= */ true, /* isPendingTask= */ true);
+        var apiDelegate = chromeAndroidTaskWithMockDeps.mMockAconfigFlaggedApiDelegate;
+        var chromeAndroidTask =
+                (ChromeAndroidTaskImpl) chromeAndroidTaskWithMockDeps.mChromeAndroidTask;
+        // Arrange: Setup display parameters.
+        var displayAndroid =
+                chromeAndroidTaskWithMockDeps.mActivityWindowAndroidMocks.mMockDisplayAndroid;
+        float dipScale = 2.0f;
+        when(displayAndroid.getDipScale()).thenReturn(dipScale);
+        // Arrange: Request SET_BOUNDS on a pending task.
+        Rect pendingBoundsInDp = new Rect(10, 20, 800, 600);
+        chromeAndroidTask.setBoundsInDp(pendingBoundsInDp);
+        // Arrange: Setup WindowAndroid.
+        var activityWindowAndroid =
+                chromeAndroidTaskWithMockDeps
+                        .mActivityWindowAndroidMocks
+                        .mMockActivityWindowAndroid;
+
+        // Act.
+        chromeAndroidTask.setActivityWindowAndroid(
+                activityWindowAndroid, chromeAndroidTaskWithMockDeps.mMockTabModel);
+
+        // Assert.
+        Rect expectedBoundsInPx = DisplayUtil.scaleToEnclosingRect(pendingBoundsInDp, dipScale);
+        var boundsCaptor = ArgumentCaptor.forClass(Rect.class);
+        verify(apiDelegate).moveTaskTo(any(), anyInt(), boundsCaptor.capture());
+        assertEquals(expectedBoundsInPx, boundsCaptor.getValue());
+    }
+
+    /**
+     * This method sets up mock insets and window metrics to simulate and return maximized window
+     * bounds when {@link WindowManager#getMaximumWindowMetrics()} is invoked from {@code
+     * mockWindowManager}.
+     */
+    private static Rect mockMaximizedBounds(WindowManager mockWindowManager) {
+        var mockMaxWindowMetrics = mock(WindowMetrics.class);
+        var mockMaxWindowInsets = mock(WindowInsets.class);
+        when(mockWindowManager.getMaximumWindowMetrics()).thenReturn(mockMaxWindowMetrics);
+        when(mockMaxWindowMetrics.getWindowInsets()).thenReturn(mockMaxWindowInsets);
+        var tappableInsets = Insets.of(0, 10, 0, 20);
+        when(mockMaxWindowInsets.getInsets(WindowInsets.Type.tappableElement()))
+                .thenReturn(tappableInsets);
+        var fullscreenBounds = new Rect(0, 0, 1920, 1080);
+        when(mockMaxWindowMetrics.getBounds()).thenReturn(fullscreenBounds);
+        return new Rect(0, 10, 1920, 1060);
+    }
 }
diff --git a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskTrackerImplUnitTest.java b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskTrackerImplUnitTest.java
index 9fb0c15..7105cc3 100644
--- a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskTrackerImplUnitTest.java
+++ b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskTrackerImplUnitTest.java
@@ -20,6 +20,7 @@
 import static org.mockito.Mockito.when;
 
 import static org.chromium.build.NullUtil.assertNonNull;
+import static org.chromium.build.NullUtil.assumeNonNull;
 import static org.chromium.chrome.browser.ui.browser_window.ChromeAndroidTaskTracker.EXTRA_PENDING_BROWSER_WINDOW_TASK_ID;
 
 import android.app.ActivityManager;
@@ -47,6 +48,7 @@
 import org.chromium.base.FakeTimeTestRule;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.lifecycle.TopResumedActivityChangedWithNativeObserver;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -582,4 +584,60 @@
         verify(activityManager2, never()).moveTaskToFront(eq(task2.getId()), anyInt());
         verify(activityManager1).moveTaskToFront(eq(task1.getId()), anyInt());
     }
+
+    @Test
+    public void obtainTask_fromPendingState_dispatchesPendingShowInactive() {
+        doTestDispatchPendingShowInactiveOrDeactivate(PendingAction.SHOW_INACTIVE);
+    }
+
+    @Test
+    public void obtainTask_fromPendingState_dispatchesPendingDeactivate() {
+        doTestDispatchPendingShowInactiveOrDeactivate(PendingAction.DEACTIVATE);
+    }
+
+    private void doTestDispatchPendingShowInactiveOrDeactivate(@PendingAction int action) {
+        assert action == PendingAction.SHOW_INACTIVE || action == PendingAction.DEACTIVATE;
+        // Arrange: Create live task and make it the top resumed task.
+        int initialTopResumedTaskId = 0;
+        var initialTopResumedTask =
+                obtainTaskWithMockDeps(initialTopResumedTaskId, /* pendingId= */ null);
+        initialTopResumedTask.onTopResumedActivityChangedWithNative(true);
+        var mockWindowAndroid =
+                assumeNonNull(initialTopResumedTask.getActivityWindowAndroidForTesting());
+        var mockActivity = assumeNonNull(mockWindowAndroid.getActivity().get());
+        var mockActivityManager =
+                (ActivityManager) mockActivity.getSystemService(Context.ACTIVITY_SERVICE);
+        // Arrange: Create pending task.
+        var mockParams =
+                ChromeAndroidTaskUnitTestSupport.createMockAndroidBrowserWindowCreateParams();
+        var pendingTask = mChromeAndroidTaskTracker.createPendingTask(mockParams);
+        // Arrange: Request SHOW_INACTIVE or DEACTIVATE on the pending task.
+        if (action == PendingAction.SHOW_INACTIVE) {
+            pendingTask.showInactive();
+        } else {
+            pendingTask.deactivate();
+        }
+
+        // Act: Simulate newly created activity gains focus.
+        mFakeTime.advanceMillis(100);
+        var newTask = obtainTaskWithMockDeps(/* taskId= */ 2, pendingTask.getPendingId());
+        newTask.onTopResumedActivityChangedWithNative(true);
+
+        // Assert: Penultimately activated task gets activated.
+        verify(mockActivityManager).moveTaskToFront(initialTopResumedTaskId, 0);
+    }
+
+    private ChromeAndroidTaskImpl obtainTaskWithMockDeps(int taskId, @Nullable Integer pendingId) {
+        var mockActivityWindowAndroid =
+                ChromeAndroidTaskUnitTestSupport.createMockActivityWindowAndroid(taskId);
+        var mockProfile = mock(Profile.class);
+        var mockTabModel = mock(TabModel.class);
+        when(mockTabModel.getProfile()).thenReturn(mockProfile);
+        return (ChromeAndroidTaskImpl)
+                mChromeAndroidTaskTracker.obtainTask(
+                        BrowserWindowType.NORMAL,
+                        mockActivityWindowAndroid,
+                        mockTabModel,
+                        pendingId);
+    }
 }
diff --git a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/PendingActionManager.java b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/PendingActionManager.java
index 6ab1e6d4..49055d7f 100644
--- a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/PendingActionManager.java
+++ b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/PendingActionManager.java
@@ -77,13 +77,22 @@
      * requested) or after a primary action.
      */
     @GuardedBy("mPendingActionsLock")
-    private final @PendingAction int[] mPendingActions = {PendingAction.NONE, PendingAction.NONE};
+    private @PendingAction int[] mPendingActions = {PendingAction.NONE, PendingAction.NONE};
 
-    /** Tracks the size a window should have when it's fully initialized. */
+    /**
+     * Tracks the size a window should have when it is fully initialized based on a SET_BOUNDS
+     * request.
+     */
     @GuardedBy("mPendingActionsLock")
     private @Nullable Rect mPendingBoundsInDp;
 
     /**
+     * Tracks the size a window should have when it is fully initialized based on a RESTORE request.
+     */
+    @GuardedBy("mPendingActionsLock")
+    private @Nullable Rect mPendingRestoredBoundsInDp;
+
+    /**
      * Requests an action to be performed on the pending task. Use this for actions that do not
      * require an input.
      *
@@ -127,6 +136,21 @@
         requestGlobalOverrideAction(PendingAction.SET_BOUNDS);
         synchronized (mPendingActionsLock) {
             mPendingBoundsInDp = boundsInDp;
+            // Cache last requested bounds for potential subsequent restoration. Pending restored
+            // bounds will be cleared after all pending actions are dispatched.
+            mPendingRestoredBoundsInDp = mPendingBoundsInDp;
+        }
+    }
+
+    @Nullable Rect getPendingBoundsInDp() {
+        synchronized (mPendingActionsLock) {
+            return mPendingBoundsInDp;
+        }
+    }
+
+    @Nullable Rect getPendingRestoredBoundsInDp() {
+        synchronized (mPendingActionsLock) {
+            return mPendingRestoredBoundsInDp;
         }
     }
 
@@ -145,6 +169,17 @@
         }
     }
 
+    @PendingAction
+    int[] getAndClearPendingActions() {
+        synchronized (mPendingActionsLock) {
+            var actions = mPendingActions;
+            mPendingActions = new int[] {PendingAction.NONE, PendingAction.NONE};
+            mPendingBoundsInDp = null;
+            mPendingRestoredBoundsInDp = null;
+            return actions;
+        }
+    }
+
     private void requestShow() {
         synchronized (mPendingActionsLock) {
             // Clear lower precedence secondary action.
@@ -252,12 +287,6 @@
         }
     }
 
-    @Nullable Rect getPendingBoundsInDp() {
-        synchronized (mPendingActionsLock) {
-            return mPendingBoundsInDp;
-        }
-    }
-
     void clearPendingActionsForTesting() {
         synchronized (mPendingActionsLock) {
             mPendingActions[0] = PendingAction.NONE;
diff --git a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/PendingActionManagerUnitTest.java b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/PendingActionManagerUnitTest.java
index 1ad4897..32016f33 100644
--- a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/PendingActionManagerUnitTest.java
+++ b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/PendingActionManagerUnitTest.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.ui.browser_window;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
 import android.graphics.Rect;
@@ -125,6 +126,10 @@
                 "Bounds should be saved.",
                 TEST_SET_BOUNDS_INPUT_1,
                 mManager.getPendingBoundsInDp());
+        assertEquals(
+                "Restored bounds should be saved.",
+                TEST_SET_BOUNDS_INPUT_1,
+                mManager.getPendingRestoredBoundsInDp());
     }
 
     @Test
@@ -156,6 +161,10 @@
                 "Bounds should be updated.",
                 TEST_SET_BOUNDS_INPUT_2,
                 mManager.getPendingBoundsInDp());
+        assertEquals(
+                "Restored bounds should be updated.",
+                TEST_SET_BOUNDS_INPUT_2,
+                mManager.getPendingRestoredBoundsInDp());
     }
 
     @Test
@@ -457,8 +466,19 @@
                         "Primary action should be NONE.", PendingAction.NONE, pendingActions[0]);
             }
 
+            if (lowerPrecedenceAction == PendingAction.SET_BOUNDS) {
+                assertNull("Bounds should be cleared.", mManager.getPendingBoundsInDp());
+                assertNotNull(
+                        "Restored bounds should not be cleared.",
+                        mManager.getPendingRestoredBoundsInDp());
+            }
+
             if (action == PendingAction.SET_BOUNDS) {
                 assertEquals("Bounds should be saved.", bounds, mManager.getPendingBoundsInDp());
+                assertEquals(
+                        "Restored bounds should be saved.",
+                        bounds,
+                        mManager.getPendingRestoredBoundsInDp());
             }
         }
     }
@@ -526,6 +546,10 @@
                             "Bounds should be preserved.",
                             TEST_SET_BOUNDS_INPUT_1,
                             mManager.getPendingBoundsInDp());
+                    assertEquals(
+                            "Restored bounds should be preserved.",
+                            TEST_SET_BOUNDS_INPUT_1,
+                            mManager.getPendingRestoredBoundsInDp());
                 }
             }
         }
@@ -567,6 +591,10 @@
                             "Bounds should be saved.",
                             TEST_SET_BOUNDS_INPUT_2,
                             mManager.getPendingBoundsInDp());
+                    assertEquals(
+                            "Restored bounds should be saved.",
+                            TEST_SET_BOUNDS_INPUT_2,
+                            mManager.getPendingRestoredBoundsInDp());
                 }
             }
         }
diff --git a/chrome/browser/ui/browser_window/internal/browser_window_features.cc b/chrome/browser/ui/browser_window/internal/browser_window_features.cc
index 861062e..368345c9 100644
--- a/chrome/browser/ui/browser_window/internal/browser_window_features.cc
+++ b/chrome/browser/ui/browser_window/internal/browser_window_features.cc
@@ -49,6 +49,7 @@
 #include "chrome/browser/ui/omnibox/ai_mode_page_action_controller.h"
 #include "chrome/browser/ui/performance_controls/memory_saver_bubble_controller.h"
 #include "chrome/browser/ui/performance_controls/memory_saver_opt_in_iph_controller.h"
+#include "chrome/browser/ui/promos/ios_promo_controller.h"
 #include "chrome/browser/ui/signin/signin_view_controller.h"
 #include "chrome/browser/ui/sync/browser_synced_window_delegate.h"
 #include "chrome/browser/ui/tabs/features.h"
@@ -126,6 +127,7 @@
 #include "components/saved_tab_groups/public/features.h"
 #include "components/search/ntp_features.h"
 #include "components/search/search.h"
+#include "components/sharing_message/features.h"
 #include "content/public/common/content_constants.h"
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -419,6 +421,13 @@
           std::make_unique<ChromeLabsCoordinator>(browser);
     }
 
+    if (MobilePromoOnDesktopTypeEnabled() !=
+        MobilePromoOnDesktopPromoType::kDisabled) {
+      ios_promo_controller_ =
+          GetUserDataFactory().CreateInstance<IOSPromoController>(*browser,
+                                                                  browser);
+    }
+
     send_tab_to_self_toolbar_bubble_controller_ = std::make_unique<
         send_tab_to_self::SendTabToSelfToolbarBubbleController>(browser);
 
@@ -830,6 +839,8 @@
 
   scrim_view_controller_.reset();
 
+  ios_promo_controller_.reset();
+
   if (auto* const provider = browser_elements_->AsA<BrowserElementsViews>()) {
     provider->TearDown();
   }
diff --git a/chrome/browser/ui/browser_window/public/browser_window_features.h b/chrome/browser/ui/browser_window/public/browser_window_features.h
index e350fa4..1f49129 100644
--- a/chrome/browser/ui/browser_window/public/browser_window_features.h
+++ b/chrome/browser/ui/browser_window/public/browser_window_features.h
@@ -58,6 +58,7 @@
 class HistorySidePanelCoordinator;
 class IncognitoClearBrowsingDataDialogCoordinator;
 class ImmersiveModeController;
+class IOSPromoController;
 class LocationBarModel;
 class MemorySaverOptInIPHController;
 class PinnedToolbarActionsController;
@@ -525,6 +526,8 @@
 
   std::unique_ptr<FullscreenControlHost> fullscreen_control_host_;
 
+  std::unique_ptr<IOSPromoController> ios_promo_controller_;
+
   std::unique_ptr<lens::LensOverlayEntryPointController>
       lens_overlay_entry_point_controller_;
 
diff --git a/chrome/browser/ui/browser_window/test/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskUnitTestSupport.java b/chrome/browser/ui/browser_window/test/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskUnitTestSupport.java
index 160fc30..15b0f8c 100644
--- a/chrome/browser/ui/browser_window/test/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskUnitTestSupport.java
+++ b/chrome/browser/ui/browser_window/test/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskUnitTestSupport.java
@@ -67,9 +67,9 @@
         /**
          * Mock {@link AndroidBrowserWindow.Natives}.
          *
-         * <p>This is {@code null} if {@link #createChromeAndroidTaskWithMockDeps(int, boolean)} was
-         * called with {@code mockNatives} set to false, in which case the test is expected to run
-         * the real native code.
+         * <p>This is {@code null} if {@link #createChromeAndroidTaskWithMockDeps(int, boolean,
+         * boolean)} was called with {@code mockNatives} set to false, in which case the test is
+         * expected to run the real native code.
          */
         final AndroidBrowserWindow.@Nullable Natives mMockAndroidBrowserWindowNatives;
 
@@ -120,9 +120,10 @@
 
     private ChromeAndroidTaskUnitTestSupport() {}
 
-    /** See {@link #createChromeAndroidTaskWithMockDeps(int, boolean)}. */
+    /** See {@link #createChromeAndroidTaskWithMockDeps(int, boolean, boolean)}. */
     public static ChromeAndroidTaskWithMockDeps createChromeAndroidTaskWithMockDeps(int taskId) {
-        return createChromeAndroidTaskWithMockDeps(taskId, /* mockNatives= */ true);
+        return createChromeAndroidTaskWithMockDeps(
+                taskId, /* mockNatives= */ true, /* isPendingTask= */ false);
     }
 
     /**
@@ -132,10 +133,14 @@
      * @param mockNatives Whether to mock {@code @NativeMethods}. Set this to false if the test
      *     needs to run native code, such as in .cc unit tests. Tests that set this to false must
      *     initialize a Native ProfileManager with a valid profile.
+     * @param isPendingTask If true, the returned {@link ChromeAndroidTask} will be in the pending
+     *     state. The returned mock dependencies will not be connected with the pending {@link
+     *     ChromeAndroidTask}. To connect the mocks with the pending {@link ChromeAndroidTask}, pass
+     *     them to {@link ChromeAndroidTask#setActivityWindowAndroid}.
      * @return A new instance of {@link ChromeAndroidTaskWithMockDeps}.
      */
     public static ChromeAndroidTaskWithMockDeps createChromeAndroidTaskWithMockDeps(
-            int taskId, boolean mockNatives) {
+            int taskId, boolean mockNatives, boolean isPendingTask) {
         Profile profile =
                 mockNatives ? mock(Profile.class) : ProfileManager.getLastUsedRegularProfile();
         TabModel tabModel = mock(TabModel.class);
@@ -144,11 +149,19 @@
         var activityWindowAndroidMocks = createActivityWindowAndroidMocks(taskId);
         var mockAndroidBrowserWindowNatives =
                 mockNatives ? createMockAndroidBrowserWindowNatives() : null;
-        var chromeAndroidTask =
-                new ChromeAndroidTaskImpl(
-                        BrowserWindowType.NORMAL,
-                        activityWindowAndroidMocks.mMockActivityWindowAndroid,
-                        tabModel);
+        ChromeAndroidTask chromeAndroidTask;
+        if (isPendingTask) {
+            chromeAndroidTask =
+                    new ChromeAndroidTaskImpl(
+                            /* pendingId= */ IdSequencer.next(),
+                            createMockAndroidBrowserWindowCreateParams());
+        } else {
+            chromeAndroidTask =
+                    new ChromeAndroidTaskImpl(
+                            BrowserWindowType.NORMAL,
+                            activityWindowAndroidMocks.mMockActivityWindowAndroid,
+                            tabModel);
+        }
         var mockAppTask = mock(AppTask.class);
         AndroidTaskUtils.setAppTaskForTesting(mockAppTask);
         var mockApiDelegate = mock(AconfigFlaggedApiDelegate.class);
diff --git a/chrome/browser/ui/color/win/native_chrome_color_mixer_win.cc b/chrome/browser/ui/color/win/native_chrome_color_mixer_win.cc
index 87208f35..a40d2f2 100644
--- a/chrome/browser/ui/color/win/native_chrome_color_mixer_win.cc
+++ b/chrome/browser/ui/color/win/native_chrome_color_mixer_win.cc
@@ -24,7 +24,6 @@
 #include "ui/color/color_transform.h"
 #include "ui/color/win/accent_color_observer.h"
 #include "ui/gfx/color_utils.h"
-#include "ui/views/views_features.h"
 
 namespace {
 
@@ -196,17 +195,10 @@
       ui::kColorNativeHighlight};
   mixer[kColorToolbar] = {ui::kColorNativeWindow};
   mixer[kColorToolbarButtonIcon] = {kColorToolbarText};
-  const bool platform_high_contrast_ink_drop = base::FeatureList::IsEnabled(
-      views::features::kEnablePlatformHighContrastInkDrop);
-  mixer[kColorToolbarButtonIconHovered] = {
-      platform_high_contrast_ink_drop
-          ? ui::ColorId{ui::kColorNativeHighlightText}
-          : kColorToolbarText};
+  mixer[kColorToolbarButtonIconHovered] = {ui::kColorNativeHighlightText};
   mixer[kColorToolbarButtonIconInactive] = {ui::kColorNativeGrayText};
   mixer[kColorToolbarContentAreaSeparator] = {kColorToolbarText};
-  if (platform_high_contrast_ink_drop) {
-    mixer[kColorToolbarInkDrop] = {ui::kColorNativeHighlight};
-  }
+  mixer[kColorToolbarInkDrop] = {ui::kColorNativeHighlight};
   mixer[kColorToolbarSeparator] = {ui::kColorNativeWindowText};
   mixer[kColorToolbarText] = {ui::kColorNativeBtnText};
   mixer[kColorToolbarTopSeparatorFrameActive] = {kColorToolbarSeparator};
diff --git a/chrome/browser/ui/extensions/BUILD.gn b/chrome/browser/ui/extensions/BUILD.gn
index 596a2ed93..c952fa2 100644
--- a/chrome/browser/ui/extensions/BUILD.gn
+++ b/chrome/browser/ui/extensions/BUILD.gn
@@ -20,6 +20,8 @@
 
   if (enable_extensions_core) {
     sources += [
+      "controlled_home_dialog_controller.h",
+      "controlled_home_dialog_controller_interface.h",
       "extension_dialog_utils.h",
       "extension_install_ui.h",
       "extensions_dialogs.h",
@@ -37,6 +39,7 @@
     }
     public_deps += [
       "//base",
+      "//extensions/browser",
       "//extensions/common",
       "//ui/base:types",
       "//ui/base/interaction",
@@ -49,8 +52,6 @@
       "accelerator_priority.h",
       "app_launch_params.h",
       "application_launch.h",
-      "controlled_home_dialog_controller.h",
-      "controlled_home_dialog_controller_interface.h",
       "extension_action_platform_delegate.h",
       "extension_action_view_controller.h",
       "extension_enable_flow.h",
@@ -102,6 +103,7 @@
   if (enable_extensions_core) {
     sources += [
       "controlled_home_dialog.cc",
+      "controlled_home_dialog_controller.cc",
       "extension_install_blocked_dialog.cc",
       "extension_install_friction_dialog.cc",
       "extension_install_ui.cc",
@@ -152,7 +154,6 @@
     sources += [
       "app_launch_params.cc",
       "application_launch.cc",
-      "controlled_home_dialog_controller.cc",
       "extension_action_view_controller.cc",
       "extension_enable_flow.cc",
       "extension_installed_bubble_model.cc",
diff --git a/chrome/browser/ui/extensions/controlled_home_dialog_controller.cc b/chrome/browser/ui/extensions/controlled_home_dialog_controller.cc
index 0bfbaa66..7bebbc04 100644
--- a/chrome/browser/ui/extensions/controlled_home_dialog_controller.cc
+++ b/chrome/browser/ui/extensions/controlled_home_dialog_controller.cc
@@ -8,17 +8,16 @@
 
 #include "base/auto_reset.h"
 #include "base/no_destructor.h"
-#include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/settings_api_helpers.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/strings/grit/components_strings.h"
-#include "components/vector_icons/vector_icons.h"
+#include "content/public/browser/page_navigator.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/common/referrer.h"
 #include "extensions/browser/disable_reason.h"
 #include "extensions/browser/extension_prefs.h"
@@ -88,9 +87,11 @@
 
 }  // namespace
 
-ControlledHomeDialogController::ControlledHomeDialogController(Browser* browser)
-    : browser_(browser),
-      profile_(browser->profile()),
+ControlledHomeDialogController::ControlledHomeDialogController(
+    Profile* profile,
+    content::WebContents* web_contents)
+    : profile_(profile),
+      web_contents_(web_contents->GetWeakPtr()),
       extension_(GetExtensionToWarnAbout(*profile_)) {}
 
 ControlledHomeDialogController::~ControlledHomeDialogController() {
@@ -211,14 +212,14 @@
       break;
     case CLOSE_LEARN_MORE: {
       AcknowledgeExtension(*profile_, extension_->id());
-      if (!g_should_ignore_learn_more_for_testing) {
+      if (!g_should_ignore_learn_more_for_testing && web_contents_) {
         GURL learn_more_url(chrome::kExtensionControlledSettingLearnMoreURL);
-        DCHECK(learn_more_url.is_valid());
-        browser_->OpenURL(
-            content::OpenURLParams(learn_more_url, content::Referrer(),
-                                   WindowOpenDisposition::NEW_FOREGROUND_TAB,
-                                   ui::PAGE_TRANSITION_LINK, false),
-            /*navigation_handle_callback=*/{});
+        CHECK(learn_more_url.is_valid());
+        content::OpenURLParams params(learn_more_url, content::Referrer(),
+                                      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                                      ui::PAGE_TRANSITION_LINK,
+                                      /*is_renderer_initiated=*/false);
+        web_contents_->OpenURL(params, {});
       }
       break;
     }
diff --git a/chrome/browser/ui/extensions/controlled_home_dialog_controller.h b/chrome/browser/ui/extensions/controlled_home_dialog_controller.h
index a18cee6..35c9d2b4 100644
--- a/chrome/browser/ui/extensions/controlled_home_dialog_controller.h
+++ b/chrome/browser/ui/extensions/controlled_home_dialog_controller.h
@@ -14,19 +14,23 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_observer.h"
 
-class Browser;
 class Profile;
 
+namespace content {
+class WebContents;
+}  // namespace content
+
 namespace extensions {
 class Extension;
-}
+}  // namespace extensions
 
 // A bubble shown for an extension overriding the user's home page (different
 // than the NTP).
 class ControlledHomeDialogController
     : public ControlledHomeDialogControllerInterface {
  public:
-  explicit ControlledHomeDialogController(Browser* browser);
+  explicit ControlledHomeDialogController(Profile* profile,
+                                          content::WebContents* web_contents);
 
   ControlledHomeDialogController(const ControlledHomeDialogController&) =
       delete;
@@ -66,10 +70,10 @@
   // if so, closes the bubble.
   void HandleExtensionUnloadOrUninstall(const extensions::Extension* extension);
 
-  // The corresponding `Browser`.
-  raw_ptr<Browser> const browser_;
   // The corresponding `Profile`.
   raw_ptr<Profile> const profile_;
+  // The original web contents that triggered the dialog.
+  base::WeakPtr<content::WebContents> web_contents_;
   // The action taken when the bubble closed, if any.
   std::optional<CloseAction> close_action_;
   // The extension controlling the home page, if any. This is null'd out when
diff --git a/chrome/browser/ui/extensions/controlled_home_dialog_controller_unittest.cc b/chrome/browser/ui/extensions/controlled_home_dialog_controller_unittest.cc
index 03a5900..e0f37747 100644
--- a/chrome/browser/ui/extensions/controlled_home_dialog_controller_unittest.cc
+++ b/chrome/browser/ui/extensions/controlled_home_dialog_controller_unittest.cc
@@ -40,6 +40,8 @@
 
 }  // namespace
 
+// TODO(crbug.com/441590893): Use ExtensionBrowserTest which is platform
+// agnostic and doesn't depend on Browser.
 class ControlledHomeDialogControllerTest : public BrowserWithTestWindowTest {
  public:
   ControlledHomeDialogControllerTest() = default;
@@ -126,6 +128,9 @@
     extension_prefs_ = extensions::ExtensionPrefs::Get(profile());
     extension_registrar_ = extensions::ExtensionRegistrar::Get(profile());
     extension_registry_ = extensions::ExtensionRegistry::Get(profile());
+
+    // Add web contents since dialog controller needs them.
+    AddTab(browser(), GURL(url::kAboutBlankURL));
   }
 
   void TearDown() override {
@@ -179,8 +184,8 @@
   ASSERT_TRUE(browser());
   ASSERT_TRUE(profile());
 
-  auto dialog_controller =
-      std::make_unique<ControlledHomeDialogController>(browser());
+  auto dialog_controller = std::make_unique<ControlledHomeDialogController>(
+      profile(), browser()->tab_strip_model()->GetActiveWebContents());
   EXPECT_TRUE(dialog_controller->ShouldShow());
   EXPECT_EQ(extension, dialog_controller->extension_for_testing());
 
@@ -203,8 +208,8 @@
       LoadExtensionOverridingHome();
   ASSERT_TRUE(extension);
 
-  auto dialog_controller =
-      std::make_unique<ControlledHomeDialogController>(browser());
+  auto dialog_controller = std::make_unique<ControlledHomeDialogController>(
+      profile(), browser()->tab_strip_model()->GetActiveWebContents());
   EXPECT_TRUE(dialog_controller->ShouldShow());
   EXPECT_EQ(extension, dialog_controller->extension_for_testing());
 
@@ -226,8 +231,8 @@
   ASSERT_TRUE(extension);
 
   {
-    auto dialog_controller =
-        std::make_unique<ControlledHomeDialogController>(browser());
+    auto dialog_controller = std::make_unique<ControlledHomeDialogController>(
+        profile(), browser()->tab_strip_model()->GetActiveWebContents());
     EXPECT_TRUE(dialog_controller->ShouldShow());
     EXPECT_EQ(extension, dialog_controller->extension_for_testing());
 
@@ -243,8 +248,8 @@
   EXPECT_FALSE(IsExtensionAcknowledged(extension->id()));
 
   {
-    auto dialog_controller =
-        std::make_unique<ControlledHomeDialogController>(browser());
+    auto dialog_controller = std::make_unique<ControlledHomeDialogController>(
+        profile(), browser()->tab_strip_model()->GetActiveWebContents());
     // Even though the extension hasn't been acknowledged, we shouldn't show the
     // bubble twice in the same session.
     EXPECT_FALSE(dialog_controller->ShouldShow());
@@ -257,8 +262,8 @@
       LoadExtensionOverridingHome();
   ASSERT_TRUE(extension);
 
-  auto dialog_controller =
-      std::make_unique<ControlledHomeDialogController>(browser());
+  auto dialog_controller = std::make_unique<ControlledHomeDialogController>(
+      profile(), browser()->tab_strip_model()->GetActiveWebContents());
   EXPECT_TRUE(dialog_controller->ShouldShow());
   EXPECT_EQ(extension, dialog_controller->extension_for_testing());
 
@@ -280,8 +285,8 @@
 
   AcknowledgeExtension(extension->id());
 
-  auto dialog_controller =
-      std::make_unique<ControlledHomeDialogController>(browser());
+  auto dialog_controller = std::make_unique<ControlledHomeDialogController>(
+      profile(), browser()->tab_strip_model()->GetActiveWebContents());
   EXPECT_FALSE(dialog_controller->ShouldShow());
 }
 
@@ -297,8 +302,8 @@
       LoadExtensionOverridingHome(base::UTF16ToUTF8(long_name));
   ASSERT_TRUE(extension);
 
-  auto dialog_controller =
-      std::make_unique<ControlledHomeDialogController>(browser());
+  auto dialog_controller = std::make_unique<ControlledHomeDialogController>(
+      profile(), browser()->tab_strip_model()->GetActiveWebContents());
   EXPECT_TRUE(dialog_controller->ShouldShow());
 
   std::u16string bubble_text = dialog_controller->GetBodyText();
@@ -317,8 +322,8 @@
   ASSERT_TRUE(extension2);
 
   {
-    auto dialog_controller =
-        std::make_unique<ControlledHomeDialogController>(browser());
+    auto dialog_controller = std::make_unique<ControlledHomeDialogController>(
+        profile(), browser()->tab_strip_model()->GetActiveWebContents());
     EXPECT_TRUE(dialog_controller->ShouldShow());
     // The most-recently-installed extension should control the home page
     // (`extension2`).
@@ -339,8 +344,8 @@
   }
 
   {
-    auto dialog_controller =
-        std::make_unique<ControlledHomeDialogController>(browser());
+    auto dialog_controller = std::make_unique<ControlledHomeDialogController>(
+        profile(), browser()->tab_strip_model()->GetActiveWebContents());
     // Since `extension2` was removed, we shouldn't have acknowledged either
     // extension and we can re-show the bubble if the homepage is controlled
     // by another extension.
@@ -359,8 +364,8 @@
   ASSERT_TRUE(extension2);
 
   {
-    auto dialog_controller =
-        std::make_unique<ControlledHomeDialogController>(browser());
+    auto dialog_controller = std::make_unique<ControlledHomeDialogController>(
+        profile(), browser()->tab_strip_model()->GetActiveWebContents());
     EXPECT_TRUE(dialog_controller->ShouldShow());
     EXPECT_EQ(extension2, dialog_controller->extension_for_testing());
 
@@ -381,8 +386,8 @@
   {
     // The bubble shouldn't want to show (the extension that controls the home
     // page was acknowledged).
-    auto dialog_controller =
-        std::make_unique<ControlledHomeDialogController>(browser());
+    auto dialog_controller = std::make_unique<ControlledHomeDialogController>(
+        profile(), browser()->tab_strip_model()->GetActiveWebContents());
     EXPECT_FALSE(dialog_controller->ShouldShow());
   }
 
@@ -391,8 +396,8 @@
       extension2->id(), {extensions::disable_reason::DISABLE_USER_ACTION});
 
   {
-    auto dialog_controller =
-        std::make_unique<ControlledHomeDialogController>(browser());
+    auto dialog_controller = std::make_unique<ControlledHomeDialogController>(
+        profile(), browser()->tab_strip_model()->GetActiveWebContents());
     // Now a new extension controls the home page, so we should re-show the
     // bubble.
     EXPECT_TRUE(dialog_controller->ShouldShow());
@@ -407,8 +412,8 @@
           "ext", extensions::mojom::ManifestLocation::kExternalPolicy);
   ASSERT_TRUE(extension);
 
-  auto dialog_controller =
-      std::make_unique<ControlledHomeDialogController>(browser());
+  auto dialog_controller = std::make_unique<ControlledHomeDialogController>(
+      profile(), browser()->tab_strip_model()->GetActiveWebContents());
   // We still show the bubble for policy-installed extensions, but it should
   // have a policy decoration.
   EXPECT_TRUE(dialog_controller->ShouldShow());
diff --git a/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc b/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc
index 70b1c76a..6833b35 100644
--- a/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc
+++ b/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc
@@ -8,27 +8,23 @@
 
 #include "base/auto_reset.h"
 #include "build/build_config.h"
-#include "chrome/browser/extensions/extension_web_ui.h"
-#include "chrome/browser/extensions/settings_api_helpers.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/extensions/controlled_home_dialog_controller.h"
 #include "chrome/browser/ui/extensions/extension_settings_overridden_dialog.h"
-#include "chrome/browser/ui/extensions/extensions_container.h"
 #include "chrome/browser/ui/extensions/extensions_dialogs.h"
 #include "chrome/browser/ui/extensions/settings_overridden_params_providers.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
-#include "chrome/common/extensions/manifest_handlers/settings_overrides_handler.h"
 #include "chrome/common/url_constants.h"
 #include "components/prefs/pref_registry.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "content/public/browser/browser_url_handler.h"
+#include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/web_contents.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/common/constants.h"
+#include "extensions/common/manifest_handlers/chrome_url_overrides_handler.h"
+#include "ui/base/base_window.h"
 
 namespace extensions {
 
@@ -106,17 +102,19 @@
                                 PrefRegistry::NO_REGISTRATION_FLAGS);
 }
 
-void MaybeShowExtensionControlledHomeNotification(Browser* browser) {
+void MaybeShowExtensionControlledHomeNotification(
+    BrowserWindowInterface* browser,
+    content::WebContents* web_contents) {
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+  auto* profile = browser->GetProfile();
   auto bubble_delegate =
-      std::make_unique<ControlledHomeDialogController>(browser);
+      std::make_unique<ControlledHomeDialogController>(profile, web_contents);
   if (!bubble_delegate->ShouldShow()) {
     return;
   }
 
   bubble_delegate->PendingShow();
-  ShowControlledHomeDialog(browser->profile(),
-                           browser->window()->GetNativeWindow(),
+  ShowControlledHomeDialog(profile, browser->GetWindow()->GetNativeWindow(),
                            std::move(bubble_delegate));
 #endif
 }
@@ -130,25 +128,26 @@
     return;
   }
 
-  Browser* browser = chrome::FindBrowserWithTab(web_contents);
-  if (!browser) {
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  if (!profile) {
     return;
   }
 
   std::optional<ExtensionSettingsOverriddenDialog::Params> params =
-      settings_overridden_params::GetSearchOverriddenParams(browser->profile());
+      settings_overridden_params::GetSearchOverriddenParams(profile);
   if (!params) {
     return;
   }
 
   auto dialog = std::make_unique<ExtensionSettingsOverriddenDialog>(
-      std::move(*params), browser->profile());
+      std::move(*params), profile);
   if (!dialog->ShouldShow()) {
     return;
   }
 
-  ShowSettingsOverriddenDialog(std::move(dialog),
-                               browser->window()->GetNativeWindow());
+  gfx::NativeWindow parent_window = web_contents->GetTopLevelNativeWindow();
+  ShowSettingsOverriddenDialog(std::move(dialog), parent_window);
 #endif
 }
 
diff --git a/chrome/browser/ui/extensions/settings_api_bubble_helpers.h b/chrome/browser/ui/extensions/settings_api_bubble_helpers.h
index 371f03f5..ddc27144 100644
--- a/chrome/browser/ui/extensions/settings_api_bubble_helpers.h
+++ b/chrome/browser/ui/extensions/settings_api_bubble_helpers.h
@@ -8,7 +8,6 @@
 #include "base/auto_reset.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 
-class Browser;
 class BrowserWindowInterface;
 class PrefRegistrySimple;
 class Profile;
@@ -43,7 +42,9 @@
 // Shows a bubble notifying the user that the homepage is controlled by an
 // extension. This bubble is shown only on the first use of the Home button
 // after the controlling extension takes effect.
-void MaybeShowExtensionControlledHomeNotification(Browser* browser);
+void MaybeShowExtensionControlledHomeNotification(
+    BrowserWindowInterface* browser,
+    content::WebContents* web_contents);
 
 // Shows a bubble notifying the user that the search engine is controlled by an
 // extension. This bubble is shown only on the first search after the
diff --git a/chrome/browser/ui/omnibox/omnibox_controller.h b/chrome/browser/ui/omnibox/omnibox_controller.h
index 5e6b67b..bc4e268 100644
--- a/chrome/browser/ui/omnibox/omnibox_controller.h
+++ b/chrome/browser/ui/omnibox/omnibox_controller.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/compiler_specific.h"
+#include "base/memory/safety_checks.h"
 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
 #include "components/omnibox/browser/autocomplete_controller.h"
 #include "components/omnibox/browser/autocomplete_match.h"
@@ -19,6 +20,9 @@
 // This class controls the various services that can modify the content of the
 // omnibox, including `AutocompleteController` and `OmniboxEditModel`.
 class OmniboxController : public AutocompleteController::Observer {
+  // TODO(crbug.com/392015004): Remove this macro once it gets fixed.
+  ADVANCED_MEMORY_SAFETY_CHECKS();
+
  public:
   OmniboxController(OmniboxView* view,
                     std::unique_ptr<OmniboxClient> client,
diff --git a/chrome/browser/ui/omnibox/omnibox_edit_model.h b/chrome/browser/ui/omnibox/omnibox_edit_model.h
index 7a4f93e..96a785d 100644
--- a/chrome/browser/ui/omnibox/omnibox_edit_model.h
+++ b/chrome/browser/ui/omnibox/omnibox_edit_model.h
@@ -15,6 +15,7 @@
 #include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/safety_checks.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list_types.h"
 #include "base/time/time.h"
@@ -38,6 +39,9 @@
 }
 
 class OmniboxEditModel {
+  // TODO(crbug.com/392015004): Remove this macro once it gets fixed.
+  ADVANCED_MEMORY_SAFETY_CHECKS();
+
  public:
   struct State {
     State(bool user_input_in_progress,
diff --git a/chrome/browser/ui/omnibox/omnibox_view.h b/chrome/browser/ui/omnibox/omnibox_view.h
index 98af47d9..eda95d0 100644
--- a/chrome/browser/ui/omnibox/omnibox_view.h
+++ b/chrome/browser/ui/omnibox/omnibox_view.h
@@ -17,6 +17,7 @@
 #include <string>
 
 #include "base/memory/raw_ptr.h"
+#include "base/memory/safety_checks.h"
 #include "build/build_config.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/omnibox_client.h"
@@ -31,6 +32,9 @@
 class OmniboxViewMacTest;
 
 class OmniboxView {
+  // TODO(crbug.com/392015004): Remove this macro once it gets fixed.
+  ADVANCED_MEMORY_SAFETY_CHECKS();
+
  public:
   using IconFetchedCallback = base::OnceCallback<void(const gfx::Image& icon)>;
 
diff --git a/chrome/browser/ui/page_info/BUILD.gn b/chrome/browser/ui/page_info/BUILD.gn
index 0b598c4..65ec3af 100644
--- a/chrome/browser/ui/page_info/BUILD.gn
+++ b/chrome/browser/ui/page_info/BUILD.gn
@@ -130,6 +130,7 @@
     "//components/permissions:permissions_common",
     "//components/prefs",
     "//components/safe_browsing:buildflags",
+    "//components/safe_browsing/core/browser:safe_browsing_metrics_collector",
     "//components/security_interstitials/content:security_interstitial_page",
     "//components/strings:components_strings",
     "//components/subresource_filter/content/browser",
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc
index 7b3dd55..01bca057 100644
--- a/chrome/browser/ui/page_info/page_info_unittest.cc
+++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -45,6 +45,7 @@
 #include "components/permissions/features.h"
 #include "components/permissions/permission_recovery_success_rate_tracker.h"
 #include "components/safe_browsing/buildflags.h"
+#include "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h"
 #include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/subresource_filter/content/browser/subresource_filter_content_settings_manager.h"
@@ -389,6 +390,7 @@
 }
 
 TEST_F(PageInfoTest, NonFactoryDefaultAndRecentlyChangedPermissionsShown) {
+  base::HistogramTester histograms;
   GURL kEmbedded1("https://embedded1.com");
   GURL kEmbedded2("https://embedded2.com");
 
@@ -497,6 +499,17 @@
                                        /*is_one_time=*/false);
   EXPECT_EQ(expected_visible_permissions.size() + 1,
             last_permission_info_list().size());
+
+  // Changing NOTIFICATIONS from ALLOW to ASK logs the histogram.
+  histograms.ExpectTotalCount("SafeBrowsing.NotificationRevocationSource", 0);
+  page_info()->OnSitePermissionChanged(ContentSettingsType::NOTIFICATIONS,
+                                       CONTENT_SETTING_ASK,
+                                       /*requesting_origin=*/std::nullopt,
+                                       /*is_one_time=*/false);
+  histograms.ExpectUniqueSample("SafeBrowsing.NotificationRevocationSource",
+                                safe_browsing::NotificationRevocationSource::
+                                    kUserManuallyChangedSiteSetting,
+                                1);
 }
 
 // Test suite for verifying that permissions granted through Page Info are
@@ -825,6 +838,7 @@
 }
 
 TEST_F(PageInfoTest, OnPermissionsChanged) {
+  base::HistogramTester histograms;
   GURL kEmbedded("https://embedded.com");
 
   // Setup site permissions.
@@ -860,9 +874,9 @@
   // SetPermissionInfo() is called once initially, and then again every time
   // OnSitePermissionChanged() is called.
 #if !BUILDFLAG(IS_ANDROID)
-  EXPECT_CALL(*mock_ui(), SetPermissionInfoStub()).Times(8);
+  EXPECT_CALL(*mock_ui(), SetPermissionInfoStub()).Times(11);
 #else
-  EXPECT_CALL(*mock_ui(), SetPermissionInfoStub()).Times(7);
+  EXPECT_CALL(*mock_ui(), SetPermissionInfoStub()).Times(10);
 #endif
 
   // Execute code under tests.
@@ -921,6 +935,32 @@
       kEmbedded, url(), ContentSettingsType::FILE_SYSTEM_WRITE_GUARD);
   EXPECT_EQ(setting, CONTENT_SETTING_ALLOW);
 #endif
+
+  // Changing NOTIFICATIONS from ALLOW to BLOCK logs the histogram.
+  histograms.ExpectTotalCount("SafeBrowsing.NotificationRevocationSource", 0);
+  page_info()->OnSitePermissionChanged(ContentSettingsType::NOTIFICATIONS,
+                                       CONTENT_SETTING_BLOCK,
+                                       /*requesting_origin=*/std::nullopt,
+                                       /*is_one_time=*/false);
+  histograms.ExpectUniqueSample("SafeBrowsing.NotificationRevocationSource",
+                                safe_browsing::NotificationRevocationSource::
+                                    kUserManuallyChangedSiteSetting,
+                                1);
+
+  // Changing NOTIFICATIONS back to ALLOW then resetting to default (by passing
+  // in std::nullopt as `setting`) logs the histogram.
+  page_info()->OnSitePermissionChanged(ContentSettingsType::NOTIFICATIONS,
+                                       CONTENT_SETTING_ALLOW,
+                                       /*requesting_origin=*/std::nullopt,
+                                       /*is_one_time=*/false);
+  page_info()->OnSitePermissionChanged(ContentSettingsType::NOTIFICATIONS,
+                                       /*setting=*/std::nullopt,
+                                       /*requesting_origin=*/std::nullopt,
+                                       /*is_one_time=*/false);
+  histograms.ExpectUniqueSample("SafeBrowsing.NotificationRevocationSource",
+                                safe_browsing::NotificationRevocationSource::
+                                    kUserManuallyChangedSiteSetting,
+                                2);
 }
 
 TEST_F(PageInfoTest, OnChosenObjectDeleted) {
diff --git a/chrome/browser/ui/passwords/ui_utils.cc b/chrome/browser/ui/passwords/ui_utils.cc
index 8ad564e..27fe64d0 100644
--- a/chrome/browser/ui/passwords/ui_utils.cc
+++ b/chrome/browser/ui/passwords/ui_utils.cc
@@ -212,6 +212,9 @@
 #if !BUILDFLAG(IS_ANDROID)
 void NavigateToManagePasswordsPage(Browser* browser,
                                    ManagePasswordsReferrer referrer) {
+  if (!browser) {
+    return;
+  }
   base::UmaHistogramEnumeration("PasswordManager.ManagePasswordsReferrer",
                                 referrer);
   chrome::ShowPasswordManager(browser);
@@ -220,6 +223,9 @@
 void NavigateToPasswordDetailsPage(Browser* browser,
                                    const std::string& password_domain_name,
                                    ManagePasswordsReferrer referrer) {
+  if (!browser) {
+    return;
+  }
   base::UmaHistogramEnumeration("PasswordManager.ManagePasswordsReferrer",
                                 referrer);
   chrome::ShowPasswordDetailsPage(browser, password_domain_name);
diff --git a/chrome/browser/ui/promos/BUILD.gn b/chrome/browser/ui/promos/BUILD.gn
index 41956b52..319c081 100644
--- a/chrome/browser/ui/promos/BUILD.gn
+++ b/chrome/browser/ui/promos/BUILD.gn
@@ -11,29 +11,45 @@
   ]
 }
 
-source_set("impl") {
-  sources = [ "ios_promos_utils.cc" ]
-
-  deps = [
-    ":utils",
-    "//chrome/browser/feature_engagement",
-    "//chrome/browser/profiles:profile",
-    "//chrome/browser/promos:utils",
-    "//chrome/browser/segmentation_platform",
-    "//chrome/browser/sync",
-    "//chrome/browser/ui",
-    "//chrome/browser/ui/views/frame:toolbar_button_provider",
-    "//chrome/browser/ui/views/location_bar",
-    "//components/feature_engagement/public",
-    "//components/segmentation_platform/embedder/default_model",
-    "//components/segmentation_platform/public",
-    "//components/sync/service",
-  ]
-
-  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
-}
-
 if (!is_android) {
+  source_set("impl") {
+    sources = [ "ios_promos_utils.cc" ]
+
+    deps = [
+      ":utils",
+      "//chrome/browser/feature_engagement",
+      "//chrome/browser/profiles:profile",
+      "//chrome/browser/promos:utils",
+      "//chrome/browser/segmentation_platform",
+      "//chrome/browser/sync",
+      "//chrome/browser/ui",
+      "//chrome/browser/ui/views/frame:toolbar_button_provider",
+      "//chrome/browser/ui/views/location_bar",
+      "//components/feature_engagement/public",
+      "//components/segmentation_platform/embedder/default_model",
+      "//components/segmentation_platform/public",
+      "//components/sync/service",
+    ]
+  }
+
+  source_set("controller") {
+    sources = [
+      "ios_promo_controller.cc",
+      "ios_promo_controller.h",
+    ]
+
+    deps = [
+      ":service",
+      ":utils",
+      "//chrome/browser/profiles:profile",
+      "//chrome/browser/ui/browser_window",
+      "//chrome/browser/ui/views/frame",
+      "//ui/base/unowned_user_data",
+    ]
+
+    public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+  }
+
   source_set("service") {
     sources = [
       "ios_promo_trigger_service.cc",
@@ -55,6 +71,7 @@
     testonly = true
     sources = [ "ios_promo_trigger_service_unittest.cc" ]
     deps = [
+      ":controller",
       ":service",
       "//base/test:test_support",
       "//chrome/browser/promos:utils",
diff --git a/chrome/browser/ui/promos/ios_promo_controller.cc b/chrome/browser/ui/promos/ios_promo_controller.cc
new file mode 100644
index 0000000..5d360b8
--- /dev/null
+++ b/chrome/browser/ui/promos/ios_promo_controller.cc
@@ -0,0 +1,47 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/promos/ios_promo_controller.h"
+
+#include "base/functional/bind.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/promos/ios_promo_trigger_service.h"
+#include "chrome/browser/ui/promos/ios_promo_trigger_service_factory.h"
+#include "chrome/browser/ui/promos/ios_promos_utils.h"
+
+DEFINE_USER_DATA(IOSPromoController);
+
+IOSPromoController::IOSPromoController(Browser* browser)
+    : browser_(browser),
+      scoped_unowned_user_data_(browser->GetUnownedUserDataHost(), *this) {
+  IOSPromoTriggerService* service =
+      IOSPromoTriggerServiceFactory::GetForProfile(browser_->profile());
+  if (service) {
+    promo_trigger_subscription_ =
+        service->RegisterPromoCallback(base::BindRepeating(
+            &IOSPromoController::OnPromoTriggered, base::Unretained(this)));
+  }
+}
+
+IOSPromoController::~IOSPromoController() = default;
+
+// static
+IOSPromoController* IOSPromoController::From(
+    BrowserWindowInterface* browser_window_interface) {
+  return Get(browser_window_interface->GetUnownedUserDataHost());
+}
+
+void IOSPromoController::OnPromoTriggered(IOSPromoType promo_type) {
+  BrowserWindow* window = browser_->window();
+  // Don't show the promo if the window is not active or the toolbar is not
+  // visible.
+  if (!window || !window->IsActive() || !window->IsToolbarVisible()) {
+    return;
+  }
+
+  ios_promos_utils::VerifyIOSPromoEligibility(promo_type, browser_);
+}
diff --git a/chrome/browser/ui/promos/ios_promo_controller.h b/chrome/browser/ui/promos/ios_promo_controller.h
new file mode 100644
index 0000000..cd9d3504
--- /dev/null
+++ b/chrome/browser/ui/promos/ios_promo_controller.h
@@ -0,0 +1,45 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_PROMOS_IOS_PROMO_CONTROLLER_H_
+#define CHROME_BROWSER_UI_PROMOS_IOS_PROMO_CONTROLLER_H_
+
+#include "base/callback_list.h"
+#include "base/memory/raw_ptr.h"
+#include "ui/base/unowned_user_data/scoped_unowned_user_data.h"
+
+class Browser;
+class BrowserWindowInterface;
+enum class IOSPromoType;
+
+// This controller is responsible for showing iOS promos for a specific
+// browser window. An instance of this class is created for each window.
+// TODO(crbug.com/446944658): This controller is a temporary solution for
+// triggering promos. The long-term plan is to migrate the presentation logic
+// to the Browser User Education system. Once that is complete, this class and
+// its associated dependency cycle workaround in the build files can be removed.
+class IOSPromoController {
+ public:
+  DECLARE_USER_DATA(IOSPromoController);
+
+  explicit IOSPromoController(Browser* browser);
+  ~IOSPromoController();
+
+  IOSPromoController(const IOSPromoController&) = delete;
+  IOSPromoController& operator=(const IOSPromoController&) = delete;
+
+  static IOSPromoController* From(
+      BrowserWindowInterface* browser_window_interface);
+
+ private:
+  void OnPromoTriggered(IOSPromoType promo_type);
+
+  const raw_ptr<Browser> browser_;
+
+  base::CallbackListSubscription promo_trigger_subscription_;
+
+  ui::ScopedUnownedUserData<IOSPromoController> scoped_unowned_user_data_;
+};
+
+#endif  // CHROME_BROWSER_UI_PROMOS_IOS_PROMO_CONTROLLER_H_
diff --git a/chrome/browser/ui/safety_hub/BUILD.gn b/chrome/browser/ui/safety_hub/BUILD.gn
index 867c11c..e3107fb0e 100644
--- a/chrome/browser/ui/safety_hub/BUILD.gn
+++ b/chrome/browser/ui/safety_hub/BUILD.gn
@@ -35,6 +35,7 @@
     "//components/content_settings/core/browser",
     "//components/content_settings/core/common",
     "//components/keyed_service/core",
+    "//components/safe_browsing/core/browser:safe_browsing_metrics_collector",
     "//components/safe_browsing/core/browser/db:database_manager",
     "//components/safety_check:features",
     "//components/site_engagement/content",
diff --git a/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager.cc b/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager.cc
index c7276dbb..02632826 100644
--- a/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager.cc
+++ b/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager.cc
@@ -53,6 +53,7 @@
     ExecuteAbusiveNotificationAutoRevocation(
         HostContentSettingsMap* hcsm,
         GURL url,
+        safe_browsing::NotificationRevocationSource revocation_source,
         const raw_ptr<const base::Clock> clock) {
   UpdateNotificationPermission(hcsm, url,
                                ContentSetting::CONTENT_SETTING_DEFAULT);
@@ -61,8 +62,8 @@
   // revocation permission.
   content_settings::ContentSettingConstraints default_constraint(clock->Now());
   default_constraint.set_lifetime(safety_hub_util::GetCleanUpThreshold());
-  SetRevokedAbusiveNotificationPermission(hcsm, url, /*is_ignored=*/false,
-                                          default_constraint);
+  SetRevokedAbusiveNotificationPermission(
+      hcsm, url, /*is_ignored=*/false, revocation_source, default_constraint);
   content_settings_uma_util::RecordContentSettingsHistogram(
       "Settings.SafetyHub.UnusedSitePermissionsModule.AutoRevoked2",
       ContentSettingsType::NOTIFICATIONS);
@@ -74,6 +75,7 @@
         HostContentSettingsMap* hcsm,
         GURL url,
         bool is_ignored,
+        safe_browsing::NotificationRevocationSource revocation_source,
         const content_settings::ContentSettingConstraints& constraints) {
   DCHECK(url.is_valid());
   // If the `url` should be ignore during future auto revocation, then the
@@ -87,15 +89,69 @@
     PermissionRevocationRequest::UndoExemptOriginFromFutureRevocations(hcsm,
                                                                        url);
   }
-
+  base::Value::Dict revoked_value;
+  revoked_value.Set(
+      safety_hub::kRevokedStatusDictKeyStr,
+      is_ignored ? safety_hub::kIgnoreStr : safety_hub::kRevokeStr);
+  std::optional<std::string> source_str =
+      GetRevocationSourceString(revocation_source);
+  if (source_str) {
+    revoked_value.Set(kAbusiveRevocationSourceKeyStr, source_str.value());
+  }
   hcsm->SetWebsiteSettingCustomScope(
       ContentSettingsPattern::FromURLNoWildcard(url),
       ContentSettingsPattern::Wildcard(),
       ContentSettingsType::REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS,
-      base::Value(base::Value::Dict().Set(
-          safety_hub::kRevokedStatusDictKeyStr,
-          is_ignored ? safety_hub::kIgnoreStr : safety_hub::kRevokeStr)),
-      constraints);
+      base::Value(std::move(revoked_value)), constraints);
+}
+
+// static
+// `SetRevokedAbusiveNotificationPermission`, preserving `revocation_source` if
+// it exists. This method should be used for re-grant, undo of the re-grant, or
+// other scenarios where there may be an existing revocation entry for the url.
+void AbusiveNotificationPermissionsManager::
+    SetRevokedAbusiveNotificationPermission(
+        HostContentSettingsMap* hcsm,
+        GURL url,
+        bool is_ignored,
+        const content_settings::ContentSettingConstraints& constraints) {
+  safe_browsing::NotificationRevocationSource revocation_source =
+      GetRevokedAbusiveNotificationRevocationSource(hcsm, url);
+
+  SetRevokedAbusiveNotificationPermission(hcsm, url, is_ignored,
+                                          revocation_source, constraints);
+}
+
+// static
+safe_browsing::NotificationRevocationSource
+AbusiveNotificationPermissionsManager::
+    GetRevokedAbusiveNotificationRevocationSource(HostContentSettingsMap* hcsm,
+                                                  GURL setting_url) {
+  DCHECK(setting_url.is_valid());
+  base::Value stored_value =
+      safety_hub_util::GetRevokedAbusiveNotificationPermissionsSettingValue(
+          hcsm, setting_url);
+  if (stored_value.is_none()) {
+    return safe_browsing::NotificationRevocationSource::kUnknown;
+  }
+  const std::string* revocation_type =
+      stored_value.GetDict().FindString(kAbusiveRevocationSourceKeyStr);
+  if (!revocation_type) {
+    return safe_browsing::NotificationRevocationSource::kUnknown;
+  }
+  if (*revocation_type == kSocialEngineeringBlocklistStr) {
+    return safe_browsing::NotificationRevocationSource::
+        kSocialEngineeringBlocklist;
+  }
+  if (*revocation_type == kManualSafeBrowsingRevocationStr) {
+    return safe_browsing::NotificationRevocationSource::
+        kManualSafeBrowsingRevocation;
+  }
+  // Only `kSocialEngineeringBlocklist` and `kManualSafeBrowsingRevocation` are
+  // stored in `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS`, other type of
+  // `NotificationRevocationSource` should never be the reason for abusive
+  // notification revocation.
+  return safe_browsing::NotificationRevocationSource::kUnknown;
 }
 
 void AbusiveNotificationPermissionsManager::
@@ -165,6 +221,7 @@
   if (stored_value.is_none()) {
     return;
   }
+
   // Set this to true to prevent removal of revoked setting values.
   is_abusive_site_revocation_running_ = true;
   UpdateNotificationPermission(hcsm_.get(), url,
@@ -303,10 +360,15 @@
   // we got a blocklist check result in time.
   timer_.Stop();
   if (threat_type == safe_browsing::SBThreatType::SB_THREAT_TYPE_URL_PHISHING) {
-    ExecuteAbusiveNotificationAutoRevocation(hcsm_.get(), url, clock_);
-    base::UmaHistogramEnumeration("SafeBrowsing.NotificationRevocationSource",
-                                  safe_browsing::NotificationRevocationSource::
-                                      kSocialEngineeringBlocklist);
+    ExecuteAbusiveNotificationAutoRevocation(
+        hcsm_.get(), url,
+        safe_browsing::NotificationRevocationSource::
+            kSocialEngineeringBlocklist,
+        clock_);
+    safe_browsing::SafeBrowsingMetricsCollector::
+        LogSafeBrowsingNotificationRevocationSourceHistogram(
+            safe_browsing::NotificationRevocationSource::
+                kSocialEngineeringBlocklist);
   }
   // Update user pref that stores the time of the last successful blocklist
   // check.
@@ -404,3 +466,21 @@
     safe_browsing_request_clients_.clear();
   }
 }
+
+// static
+std::optional<std::string>
+AbusiveNotificationPermissionsManager::GetRevocationSourceString(
+    safe_browsing::NotificationRevocationSource source) {
+  switch (source) {
+    case safe_browsing::NotificationRevocationSource::
+        kSocialEngineeringBlocklist:
+      return kSocialEngineeringBlocklistStr;
+    case safe_browsing::NotificationRevocationSource::
+        kManualSafeBrowsingRevocation:
+      return kManualSafeBrowsingRevocationStr;
+    // Other revocation sources are not stored in
+    // REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS setting.
+    default:
+      return std::nullopt;
+  }
+}
diff --git a/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager.h b/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager.h
index 6737270..1c64dd2 100644
--- a/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager.h
+++ b/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager.h
@@ -11,6 +11,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/safe_browsing/core/browser/db/database_manager.h"
+#include "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h"
 
 class GURL;
 
@@ -19,6 +20,12 @@
 // check. After this amount of time the outstanding check will be aborted, and
 // the resource will be treated as if it were safe.
 inline constexpr int kCheckUrlTimeoutMs = 5000;
+
+// Key of the base::Value dictionary we assign to the
+// REVOKED_ABUSIVE_NOTIFICATION_PERMISSION to specify revocation reason.
+inline constexpr char kAbusiveRevocationSourceKeyStr[] = "revocation_source";
+inline constexpr char kSocialEngineeringBlocklistStr[] = "social_engineering";
+inline constexpr char kManualSafeBrowsingRevocationStr[] = "manual";
 }  // namespace
 
 namespace safe_browsing {
@@ -65,17 +72,36 @@
   static void ExecuteAbusiveNotificationAutoRevocation(
       HostContentSettingsMap* hcsm,
       GURL url,
+      safe_browsing::NotificationRevocationSource revocation_source,
       const raw_ptr<const base::Clock> clock);
 
-  // Sets the `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS` value for a url, given
-  // the constraints and whether the user wants to ignore future
-  // auto-revocation.
+  // Sets the `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS` value for a url,
+  // revocation source, given the constraints, and whether the user wants to
+  // ignore future auto-revocation.
+  static void SetRevokedAbusiveNotificationPermission(
+      HostContentSettingsMap* hcsm,
+      GURL url,
+      bool is_ignored,
+      safe_browsing::NotificationRevocationSource revocation_source,
+      const content_settings::ContentSettingConstraints& constraints = {});
+
+  // Perform `SetRevokedAbusiveNotificationPermission`, preserving revocation
+  // source if exists.
   static void SetRevokedAbusiveNotificationPermission(
       HostContentSettingsMap* hcsm,
       GURL url,
       bool is_ignored,
       const content_settings::ContentSettingConstraints& constraints = {});
 
+  // Return `NotificationRevocationSource` if there is  a
+  // `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS` setting value for the
+  // `setting_url` with the `safety_hub::kAbusiveRevocationSourceKeyStr`.
+  // Returns `NotificationRevocationSource::kUnknown` if the setting does not
+  // exist, the key does not exist, or the value is invalid.
+  static safe_browsing::NotificationRevocationSource
+  GetRevokedAbusiveNotificationRevocationSource(HostContentSettingsMap* hcsm,
+                                                GURL url);
+
   // Calls `PerformSafeBrowsingChecks` on URLs which have notifications
   // enabled and haven't been marked as a URL to be ignored.
   void CheckNotificationPermissionOrigins();
@@ -153,6 +179,8 @@
   FRIEND_TEST_ALL_PREFIXES(AbusiveNotificationPermissionsManagerTest,
                            SetRevokedAbusiveNotificationPermission);
   FRIEND_TEST_ALL_PREFIXES(AbusiveNotificationPermissionsManagerTest,
+                           SetIgnoreRevokedAbusiveNotificationPermission);
+  FRIEND_TEST_ALL_PREFIXES(AbusiveNotificationPermissionsManagerTest,
                            UndoRegrantPermissionForOriginIfNecessary);
 
   // On object creation, checks the Safe Browsing blocklist for `url_`
@@ -244,6 +272,11 @@
   // Called each time Safe Browsing checks are performed on a set of URLs.
   void ResetSafeBrowsingCheckHelpers();
 
+  // Convert `NotificationRevocationSource` to its string representation for
+  // storing in `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS`.
+  static std::optional<std::string> GetRevocationSourceString(
+      safe_browsing::NotificationRevocationSource source);
+
   // Used for interactions with the local database, when checking the blocklist.
   scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager_;
 
diff --git a/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager_unittest.cc b/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager_unittest.cc
index f1b6451016..ba08cea 100644
--- a/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager_unittest.cc
+++ b/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager_unittest.cc
@@ -110,7 +110,6 @@
   // TODO(crbug/com/342210522): When we refactor utils to be cleaner, this
   // helper will no longer be necessary.
   bool IsRevokedSettingValueRevoked(
-      AbusiveNotificationPermissionsManager* abuse_manager,
       std::string url) {
     base::Value stored_value =
         safety_hub_util::GetRevokedAbusiveNotificationPermissionsSettingValue(
@@ -169,8 +168,8 @@
             ContentSetting::CONTENT_SETTING_ASK);
   EXPECT_EQ(GetNotificationSettingValue(url2),
             ContentSetting::CONTENT_SETTING_ASK);
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url1));
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url2));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url1));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url2));
 
   VerifyTimeoutCallbackNotCalled();
 
@@ -208,7 +207,7 @@
       safety_hub_util::GetRevokedAbusiveNotificationPermissionsSettingValue(
           hcsm(), GURL(url1))
           .is_none());
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url2));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url2));
 
   VerifyTimeoutCallbackNotCalled();
 
@@ -242,7 +241,7 @@
             ContentSetting::CONTENT_SETTING_ASK);
   EXPECT_EQ(GetNotificationSettingValue(url3),
             ContentSetting::CONTENT_SETTING_ASK);
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url1));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url1));
   EXPECT_TRUE(
       safety_hub_util::GetRevokedAbusiveNotificationPermissionsSettingValue(
           hcsm(), GURL(url2))
@@ -281,7 +280,7 @@
             ContentSetting::CONTENT_SETTING_ALLOW);
   EXPECT_EQ(GetNotificationSettingValue(url3),
             ContentSetting::CONTENT_SETTING_ALLOW);
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url1));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url1));
   EXPECT_TRUE(safety_hub_util::IsAbusiveNotificationRevocationIgnored(
       hcsm(), GURL(url2)));
   EXPECT_TRUE(safety_hub_util::IsAbusiveNotificationRevocationIgnored(
@@ -329,8 +328,8 @@
             ContentSetting::CONTENT_SETTING_ASK);
   EXPECT_EQ(GetNotificationSettingValue(url2),
             ContentSetting::CONTENT_SETTING_ASK);
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url1));
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url2));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url1));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url2));
 
   // Make sure that when we regrant url1, it is not automatically revoked again.
   manager.RegrantPermissionForOriginIfNecessary(GURL(url1));
@@ -344,7 +343,7 @@
             ContentSetting::CONTENT_SETTING_ASK);
   EXPECT_TRUE(safety_hub_util::IsAbusiveNotificationRevocationIgnored(
       hcsm(), GURL(url1)));
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url2));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url2));
 
   // Running period checks again should still not include url1.
   RunUntilSafeBrowsingChecksComplete(&manager);
@@ -358,7 +357,7 @@
             ContentSetting::CONTENT_SETTING_ASK);
   EXPECT_TRUE(safety_hub_util::IsAbusiveNotificationRevocationIgnored(
       hcsm(), GURL(url1)));
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url2));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url2));
 
   // Check that the correct metric is reported.
   auto ukm_entries = ukm_recorder.GetEntriesByName(
@@ -447,8 +446,11 @@
       0u);
 
   AbusiveNotificationPermissionsManager::
-      SetRevokedAbusiveNotificationPermission(hcsm(), GURL(url1),
-                                              /*is_ignored=*/false);
+      SetRevokedAbusiveNotificationPermission(
+          hcsm(), GURL(url1),
+          /*is_ignored=*/false,
+          safe_browsing::NotificationRevocationSource::
+              kSocialEngineeringBlocklist);
   content_settings =
       safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm());
   EXPECT_EQ(content_settings.size(), 1u);
@@ -457,15 +459,18 @@
             ContentSetting::CONTENT_SETTING_ASK);
   EXPECT_EQ(GetNotificationSettingValue(url2),
             ContentSetting::CONTENT_SETTING_ASK);
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url1));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url1));
   EXPECT_TRUE(
       safety_hub_util::GetRevokedAbusiveNotificationPermissionsSettingValue(
           hcsm(), GURL(url2))
           .is_none());
 
   AbusiveNotificationPermissionsManager::
-      SetRevokedAbusiveNotificationPermission(hcsm(), GURL(url2),
-                                              /*is_ignored=*/false);
+      SetRevokedAbusiveNotificationPermission(
+          hcsm(), GURL(url2),
+          /*is_ignored=*/false,
+          safe_browsing::NotificationRevocationSource::
+              kSocialEngineeringBlocklist);
   content_settings =
       safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm());
   EXPECT_EQ(content_settings.size(), 2u);
@@ -475,8 +480,64 @@
             ContentSetting::CONTENT_SETTING_ASK);
   EXPECT_EQ(GetNotificationSettingValue(url2),
             ContentSetting::CONTENT_SETTING_ASK);
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url1));
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url2));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url1));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url2));
+  EXPECT_EQ(
+      safe_browsing::NotificationRevocationSource::kSocialEngineeringBlocklist,
+      AbusiveNotificationPermissionsManager::
+          GetRevokedAbusiveNotificationRevocationSource(hcsm(), GURL(url1)));
+  EXPECT_EQ(
+      safe_browsing::NotificationRevocationSource::kSocialEngineeringBlocklist,
+      AbusiveNotificationPermissionsManager::
+          GetRevokedAbusiveNotificationRevocationSource(hcsm(), GURL(url2)));
+}
+
+TEST_F(
+    AbusiveNotificationPermissionsManagerTest,
+    SetRevokedAbusiveNotificationPermissionMaintainsExistingRevocationSource) {
+  // Simulate existing revoked notification setting without revocation source.
+  AddRevokedAbusiveNotification(url1, ContentSetting::CONTENT_SETTING_ASK,
+                                /*is_ignored=*/false);
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url1));
+  // Simulate existing revoked notification setting with revocation source
+  // `kManualSafeBrowsingRevocationStr`.
+  AddAbusiveNotification(url2, ContentSetting::CONTENT_SETTING_ALLOW);
+  content_settings::ContentSettingConstraints constraint;
+  hcsm()->SetWebsiteSettingDefaultScope(
+      GURL(url2), GURL(url2), revoked_notifications_type,
+      base::Value(
+          base::Value::Dict()
+              .Set(safety_hub::kRevokedStatusDictKeyStr, safety_hub::kRevokeStr)
+              .Set(kAbusiveRevocationSourceKeyStr,
+                   kManualSafeBrowsingRevocationStr)),
+      constraint);
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url2));
+  EXPECT_EQ(
+      safe_browsing::NotificationRevocationSource::
+          kManualSafeBrowsingRevocation,
+      AbusiveNotificationPermissionsManager::
+          GetRevokedAbusiveNotificationRevocationSource(hcsm(), GURL(url2)));
+
+  AbusiveNotificationPermissionsManager::
+      SetRevokedAbusiveNotificationPermission(hcsm(), GURL(url1),
+                                              /*is_ignored=*/true);
+  AbusiveNotificationPermissionsManager::
+      SetRevokedAbusiveNotificationPermission(hcsm(), GURL(url2),
+                                              /*is_ignored=*/true);
+
+  // Verify the setting values are changed to ignored.
+  EXPECT_FALSE(IsRevokedSettingValueRevoked(url1));
+  EXPECT_FALSE(IsRevokedSettingValueRevoked(url2));
+  // Verify that the revocation sources are maintained.
+  EXPECT_EQ(
+      safe_browsing::NotificationRevocationSource::kUnknown,
+      AbusiveNotificationPermissionsManager::
+          GetRevokedAbusiveNotificationRevocationSource(hcsm(), GURL(url1)));
+  EXPECT_EQ(
+      safe_browsing::NotificationRevocationSource::
+          kManualSafeBrowsingRevocation,
+      AbusiveNotificationPermissionsManager::
+          GetRevokedAbusiveNotificationRevocationSource(hcsm(), GURL(url2)));
 }
 
 TEST_F(AbusiveNotificationPermissionsManagerTest,
@@ -506,7 +567,7 @@
             ContentSetting::CONTENT_SETTING_ASK);
   EXPECT_TRUE(safety_hub_util::IsAbusiveNotificationRevocationIgnored(
       hcsm(), GURL(url1)));
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url2));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url2));
 
   content_settings::ContentSettingConstraints constraints;
   manager.UndoRegrantPermissionForOriginIfNecessary(
@@ -520,8 +581,8 @@
             ContentSetting::CONTENT_SETTING_ASK);
   EXPECT_EQ(GetNotificationSettingValue(url2),
             ContentSetting::CONTENT_SETTING_ASK);
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url1));
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url2));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url1));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url2));
 
   // Check that the correct metric is reported.
   auto ukm_entries = ukm_recorder.GetEntriesByName(
@@ -546,6 +607,41 @@
 }
 
 TEST_F(AbusiveNotificationPermissionsManagerTest,
+       RegrantAndUndoMaintainExistingRevocationSource) {
+  // Simulate existing revoked notification setting with revocation source
+  // `kManualSafeBrowsingRevocationStr`.
+  AddAbusiveNotification(url1, ContentSetting::CONTENT_SETTING_ASK);
+  content_settings::ContentSettingConstraints constraint;
+  hcsm()->SetWebsiteSettingDefaultScope(
+      GURL(url1), GURL(url1), revoked_notifications_type,
+      base::Value(
+          base::Value::Dict()
+              .Set(safety_hub::kRevokedStatusDictKeyStr, safety_hub::kRevokeStr)
+              .Set(kAbusiveRevocationSourceKeyStr,
+                   kManualSafeBrowsingRevocationStr)),
+      constraint);
+  auto manager = AbusiveNotificationPermissionsManager(
+      mock_database_manager(), hcsm(), profile()->GetTestingPrefService());
+
+  // Re-grant.
+  manager.RegrantPermissionForOriginIfNecessary(GURL(url1));
+  EXPECT_TRUE(safety_hub_util::IsAbusiveNotificationRevocationIgnored(
+      hcsm(), GURL(url1)));
+
+  // Undo re-grant.
+  content_settings::ContentSettingConstraints constraints;
+  manager.UndoRegrantPermissionForOriginIfNecessary(
+      GURL(url1), abusive_permission_types, std::move(constraints));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url1));
+
+  ASSERT_EQ(
+      safe_browsing::NotificationRevocationSource::
+          kManualSafeBrowsingRevocation,
+      AbusiveNotificationPermissionsManager::
+          GetRevokedAbusiveNotificationRevocationSource(hcsm(), GURL(url1)));
+}
+
+TEST_F(AbusiveNotificationPermissionsManagerTest,
        CheckExpirationOfRevokedAbusiveNotifications) {
   AddAbusiveNotification(url1, ContentSetting::CONTENT_SETTING_ALLOW);
   AddAbusiveNotification(url2, ContentSetting::CONTENT_SETTING_ALLOW);
@@ -570,7 +666,7 @@
             ContentSetting::CONTENT_SETTING_ASK);
   EXPECT_TRUE(safety_hub_util::IsAbusiveNotificationRevocationIgnored(
       hcsm(), GURL(url1)));
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url2));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url2));
 
   // After 40 days, the `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS` settings
   // should be cleaned up, `url1` should still have allowed notifications,
@@ -634,7 +730,7 @@
   EXPECT_TRUE(IsUrlInContentSettings(content_settings, url3));
   EXPECT_EQ(GetNotificationSettingValue(url3),
             ContentSetting::CONTENT_SETTING_ASK);
-  EXPECT_TRUE(IsRevokedSettingValueRevoked(&manager, url3));
+  EXPECT_TRUE(IsRevokedSettingValueRevoked(url3));
   histogram_tester.ExpectTotalCount(
       safety_hub::kBlocklistCheckCountHistogramName, /* expected_count */ 3);
   histogram_tester.ExpectBucketCount(
diff --git a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.cc b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.cc
index dd873ec..6e4ac9c 100644
--- a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.cc
+++ b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.cc
@@ -25,6 +25,7 @@
 #include "components/content_settings/core/common/content_settings_utils.h"
 #include "components/content_settings/core/common/features.h"
 #include "components/permissions/notifications_engagement_service.h"
+#include "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h"
 #include "components/site_engagement/content/site_engagement_service.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "url/gurl.h"
@@ -571,6 +572,10 @@
       "Settings.SafetyHub.DisruptiveNotificationRevocations."
       "HasReportedMetricsBeforeRevocation",
       revocation_entry.has_reported_proposal);
+  safe_browsing::SafeBrowsingMetricsCollector::
+      LogSafeBrowsingNotificationRevocationSourceHistogram(
+          safe_browsing::NotificationRevocationSource::
+              kDisruptiveAutoRevocation);
 }
 
 void DisruptiveNotificationPermissionsManager::DisplayNotification() {
diff --git a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager_unittest.cc b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager_unittest.cc
index 0dd7f0e0..d4bc006 100644
--- a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager_unittest.cc
+++ b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager_unittest.cc
@@ -21,6 +21,7 @@
 #include "components/content_settings/core/test/content_settings_mock_provider.h"
 #include "components/content_settings/core/test/content_settings_test_utils.h"
 #include "components/permissions/constants.h"
+#include "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h"
 #include "components/safe_browsing/core/common/features.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/public/browser/browser_context.h"
@@ -51,6 +52,8 @@
     "NotificationCount";
 constexpr char kRevokedWebsitesCountHistogram[] =
     "Settings.SafetyHub.DisruptiveNotificationRevocations.RevokedWebsitesCount";
+constexpr char kSafeBrowsingNotificationRevocationSourceHistogram[] =
+    "SafeBrowsing.NotificationRevocationSource";
 
 base::TimeDelta GetRevocationsLifetime() {
   return content_settings::features::
@@ -443,6 +446,10 @@
       "Settings.SafetyHub.DisruptiveNotificationRevocations."
       "HasReportedMetricsBeforeRevocation",
       true, 1);
+  t.ExpectBucketCount(
+      kSafeBrowsingNotificationRevocationSourceHistogram,
+      safe_browsing::NotificationRevocationSource::kDisruptiveAutoRevocation,
+      1);
 
   // After that, no new metrics are reported since there is no notification
   // content setting exception.
diff --git a/chrome/browser/ui/startup/BUILD.gn b/chrome/browser/ui/startup/BUILD.gn
index 44a321d5..077f5b5 100644
--- a/chrome/browser/ui/startup/BUILD.gn
+++ b/chrome/browser/ui/startup/BUILD.gn
@@ -145,6 +145,7 @@
     "//sandbox/policy",
     "//services/network/public/cpp:flags_and_switches",
     "//services/webnn:webnn_switches",
+    "//third_party/abseil-cpp:absl",
     "//third_party/blink/public:runtime_features_for_public",
     "//ui/base",
     "//ui/gfx",
diff --git a/chrome/browser/ui/startup/bad_flags_prompt.cc b/chrome/browser/ui/startup/bad_flags_prompt.cc
index 03133213..c6de9b8 100644
--- a/chrome/browser/ui/startup/bad_flags_prompt.cc
+++ b/chrome/browser/ui/startup/bad_flags_prompt.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <string>
 #include <string_view>
+#include <variant>
 
 #include "base/base_switches.h"
 #include "base/command_line.h"
@@ -40,6 +41,7 @@
 #include "media/media_buildflags.h"
 #include "sandbox/policy/switches.h"
 #include "services/network/public/cpp/network_switches.h"
+#include "third_party/abseil-cpp/absl/functional/overload.h"
 #include "third_party/blink/public/common/features_generated.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -87,7 +89,6 @@
 #if BUILDFLAG(IS_WIN)
     sandbox::policy::switches::kAllowThirdPartyModules,
 #endif
-    switches::kDisableSiteIsolation,
     switches::kDisableWebSecurity,
     switches::kSingleProcess,
 
@@ -207,19 +208,29 @@
 };
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-// Dangerous feature flags in about:flags for which to display a warning that
-// "stability and security will suffer".
-static const base::Feature* kBadFeatureFlagsInAboutFlags[] = {
-    // This feature enables developer mode support for Isolated Web Apps.
-    &features::kIsolatedWebAppDevMode,
+// Dangerous flags that can be enabled in about:flags, for which to display a
+// warning that "stability and security will suffer".
+//
+// A flag should be listed here if it is available in about:flags on Android.
+// Flags that are only available on the command line and not in about:flags
+// should be listed in `kBadFlags` above, which is only checked on desktop
+// platforms. This is because command-line flags cannot be set by users on
+// non-rooted Android devices, so we avoid showing a warning for them.
+static const std::variant<const base::Feature*, const char*>
+    kBadFeatureFlagsInAboutFlags[] = {
+        // This feature enables developer mode support for Isolated Web Apps.
+        &features::kIsolatedWebAppDevMode,
+
+        // This flag disables site isolation.
+        switches::kDisableSiteIsolation,
 
 #if BUILDFLAG(IS_ANDROID)
-    &chrome::android::kCommandLineOnNonRooted,
+        &chrome::android::kCommandLineOnNonRooted,
 #endif
 
-    // This flag disables security for the Page Embedded Permission Control, for
-    // testing purposes. Can only be enabled via the command line.
-    &blink::features::kBypassPepcSecurityForTesting,
+        // This flag disables security for the Page Embedded Permission Control,
+        // for testing purposes. Can only be enabled via the command line.
+        &blink::features::kBypassPepcSecurityForTesting,
 };
 
 void ShowBadFlagsInfoBarHelper(content::WebContents* web_contents,
@@ -251,15 +262,33 @@
   }
 #endif
 
-  for (const base::Feature* feature : kBadFeatureFlagsInAboutFlags) {
-    if (base::FeatureList::IsEnabled(*feature)) {
+  for (const auto& flag_or_feature : kBadFeatureFlagsInAboutFlags) {
+    std::string bad_flag_name = std::visit(
+        absl::Overload{[](const base::Feature* feature) -> std::string {
+                         if (feature &&
+                             base::FeatureList::IsEnabled(*feature)) {
+                           return feature->name;
+                         }
+                         return "";
+                       },
+                       [](const char* flag_name) -> std::string {
+                         if (flag_name &&
+                             base::CommandLine::ForCurrentProcess()->HasSwitch(
+                                 flag_name)) {
+                           return flag_name;
+                         }
+                         return "";
+                       }},
+        flag_or_feature);
+
+    if (!bad_flag_name.empty()) {
 #if BUILDFLAG(IS_ANDROID)
       ShowBadFlagsSnackbar(web_contents, l10n_util::GetStringFUTF16(
                                              IDS_BAD_FEATURES_WARNING_MESSAGE,
-                                             base::UTF8ToUTF16(feature->name)));
+                                             base::UTF8ToUTF16(bad_flag_name)));
 #else
       ShowBadFlagsInfoBarHelper(web_contents, IDS_BAD_FEATURES_WARNING_MESSAGE,
-                                feature->name);
+                                bad_flag_name);
 #endif
       return;
     }
diff --git a/chrome/browser/ui/tabs/tab_list_bridge.cc b/chrome/browser/ui/tabs/tab_list_bridge.cc
index a6c3315..1e99f5f 100644
--- a/chrome/browser/ui/tabs/tab_list_bridge.cc
+++ b/chrome/browser/ui/tabs/tab_list_bridge.cc
@@ -208,6 +208,15 @@
     case TabStripModelChange::kSelectionOnly:
       break;
   }
+
+  if (selection.active_tab_changed()) {
+    tabs::TabInterface* tab = tab_strip_->GetActiveTab();
+    if (tab) {
+      for (auto& observer : observers_) {
+        observer.OnActiveTabChanged(tab);
+      }
+    }
+  }
 }
 
 // static
diff --git a/chrome/browser/ui/tabs/tab_list_bridge_browsertest.cc b/chrome/browser/ui/tabs/tab_list_bridge_browsertest.cc
index 2d1b1e7..356aa44a 100644
--- a/chrome/browser/ui/tabs/tab_list_bridge_browsertest.cc
+++ b/chrome/browser/ui/tabs/tab_list_bridge_browsertest.cc
@@ -4,6 +4,10 @@
 
 #include "chrome/browser/ui/tabs/tab_list_bridge.h"
 
+#include <deque>
+
+#include "base/check_op.h"
+#include "base/memory/raw_ptr.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -17,6 +21,52 @@
 // TabListInterface browsertest, and use it on all relevant platforms?
 using TabListBridgeBrowserTest = InProcessBrowserTest;
 
+// Represents an event reported via `TabListInterfaceObserver`.
+struct Event {
+  enum class Type {
+    TAB_ADDED,
+    ACTIVE_TAB_CHANGED,
+  };
+  Type type;
+  raw_ptr<tabs::TabInterface> tab;
+};
+
+// A fake implementation of TabListInterfaceObserver that records callback
+// invocations as `Event`s.
+class FakeObserver : public TabListInterfaceObserver {
+ public:
+  explicit FakeObserver(TabListInterface* tab_list) {
+    observation_.Observe(tab_list);
+  }
+
+  // Reads an event of the specified type, discarding events of other types
+  // reported before the event.
+  Event ReadEvent(Event::Type type) {
+    while (true) {
+      CHECK(!events_.empty()) << "No event of the specified type reported";
+      Event event = std::move(events_.front());
+      events_.pop_front();
+      if (event.type == type) {
+        return event;
+      }
+    }
+  }
+
+  // TabListInterfaceObserver:
+  void OnTabAdded(tabs::TabInterface* tab, int index) override {
+    events_.push_back(Event{Event::Type::TAB_ADDED, tab});
+  }
+
+  void OnActiveTabChanged(tabs::TabInterface* tab) override {
+    events_.push_back(Event{Event::Type::ACTIVE_TAB_CHANGED, tab});
+  }
+
+ private:
+  std::deque<Event> events_;
+  base::ScopedObservation<TabListInterface, TabListInterfaceObserver>
+      observation_{this};
+};
+
 // A helpful matcher for tabs having an expected URL. Since we assume the
 // TabInterface works, this is sufficient to meaningfully describe tabs in
 // expectations.
@@ -310,3 +360,63 @@
   EXPECT_THAT(destination_list_interface->GetAllTabs(),
               testing::ElementsAre(testing::_, MatchesTab(url2)));
 }
+
+IN_PROC_BROWSER_TEST_F(TabListBridgeBrowserTest, Observer_OnTabAdded) {
+  const GURL url1("http://one.example");
+  const GURL url2("http://two.example");
+  const GURL url3("http://three.example");
+
+  TabListInterface* tab_list_interface = TabListBridge::From(browser());
+  ASSERT_TRUE(tab_list_interface);
+
+  FakeObserver observer(tab_list_interface);
+
+  // Navigate to one.example in the current tab.
+  ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url1, WindowOpenDisposition::CURRENT_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+
+  // Open a new tab in the background.
+  ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url2, WindowOpenDisposition::NEW_BACKGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+
+  // Open a new tab in the foreground.
+  ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url3, WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+
+  // We should have received two TAB_ADDED events.
+  EXPECT_THAT(observer.ReadEvent(Event::Type::TAB_ADDED).tab, MatchesTab(url2));
+  EXPECT_THAT(observer.ReadEvent(Event::Type::TAB_ADDED).tab, MatchesTab(url3));
+}
+
+IN_PROC_BROWSER_TEST_F(TabListBridgeBrowserTest, Observer_OnActiveTabChanged) {
+  const GURL url1("http://one.example");
+  const GURL url2("http://two.example");
+  const GURL url3("http://three.example");
+
+  TabListInterface* tab_list_interface = TabListBridge::From(browser());
+  ASSERT_TRUE(tab_list_interface);
+
+  FakeObserver observer(tab_list_interface);
+
+  // Navigate to one.example in the current tab.
+  ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url1, WindowOpenDisposition::CURRENT_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+
+  // Open a new tab in the background.
+  ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url2, WindowOpenDisposition::NEW_BACKGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+
+  // Open a new tab in the foreground.
+  ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url3, WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+
+  // We should have received single ACTIVE_TAB_CHANGED event.
+  EXPECT_THAT(observer.ReadEvent(Event::Type::ACTIVE_TAB_CHANGED).tab,
+              MatchesTab(url3));
+}
diff --git a/chrome/browser/ui/tabs/tab_list_interface.h b/chrome/browser/ui/tabs/tab_list_interface.h
index c458b913..2f8bd8b 100644
--- a/chrome/browser/ui/tabs/tab_list_interface.h
+++ b/chrome/browser/ui/tabs/tab_list_interface.h
@@ -10,6 +10,7 @@
 #include <set>
 #include <vector>
 
+#include "base/scoped_observation_traits.h"
 #include "build/android_buildflags.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tabs/public/tab_interface.h"
@@ -138,4 +139,20 @@
                                     int destination_index) = 0;
 };
 
+namespace base {
+
+template <>
+struct ScopedObservationTraits<TabListInterface, TabListInterfaceObserver> {
+  static void AddObserver(TabListInterface* tab_list,
+                          TabListInterfaceObserver* observer) {
+    tab_list->AddTabListInterfaceObserver(observer);
+  }
+  static void RemoveObserver(TabListInterface* tab_list,
+                             TabListInterfaceObserver* observer) {
+    tab_list->RemoveTabListInterfaceObserver(observer);
+  }
+};
+
+}  // namespace base
+
 #endif  // CHROME_BROWSER_UI_TABS_TAB_LIST_INTERFACE_H_
diff --git a/chrome/browser/ui/tabs/tab_list_interface_observer.h b/chrome/browser/ui/tabs/tab_list_interface_observer.h
index ae6ff52e..d77e90d 100644
--- a/chrome/browser/ui/tabs/tab_list_interface_observer.h
+++ b/chrome/browser/ui/tabs/tab_list_interface_observer.h
@@ -21,7 +21,11 @@
   // on Android platforms, such as if a tab that was closed is re-introduced
   // (see also tabClosureUndone() here:
   // https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelObserver.java;drc=e2bb611334ebe2b1cbe519ff183f5178896b8c55;l=140).
-  void OnTabAdded(tabs::TabInterface* tab, int index) {}
+  virtual void OnTabAdded(tabs::TabInterface* tab, int index) {}
+
+  // Called when the active tab changed. `tab` is the new active tab and is
+  // never null.
+  virtual void OnActiveTabChanged(tabs::TabInterface* tab) {}
 };
 
 #endif  // CHROME_BROWSER_UI_TABS_TAB_LIST_INTERFACE_OBSERVER_H_
diff --git a/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc b/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
index e000fbf..9366bb2 100644
--- a/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
+++ b/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
@@ -121,7 +121,6 @@
     feature_list_.InitWithFeatures(
         {toast_features::kLinkCopiedToast, toast_features::kImageCopiedToast,
          toast_features::kReadingListToast,
-         toast_features::kPinnedTabToastOnClose,
          plus_addresses::features::kPlusAddressesEnabled},
         // Disable `kAiModeOmniboxEntryPoint` as it changes the focus and popup
         // opening order of the omnibox. If it launches, updates the tests to
diff --git a/chrome/browser/ui/toasts/toast_features.cc b/chrome/browser/ui/toasts/toast_features.cc
index f6cf3e2..e470a71 100644
--- a/chrome/browser/ui/toasts/toast_features.cc
+++ b/chrome/browser/ui/toasts/toast_features.cc
@@ -31,9 +31,6 @@
 // Enabled the clear browsing data confirmation toast.
 BASE_FEATURE(kClearBrowsingDataToast, base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Enables the pinned tab closing notification toast.
-BASE_FEATURE(kPinnedTabToastOnClose, base::FEATURE_ENABLED_BY_DEFAULT);
-
 // static
 bool IsEnabled(const base::Feature& feature) {
   return base::FeatureList::IsEnabled(feature);
diff --git a/chrome/browser/ui/toasts/toast_features.h b/chrome/browser/ui/toasts/toast_features.h
index e412544..875a0339 100644
--- a/chrome/browser/ui/toasts/toast_features.h
+++ b/chrome/browser/ui/toasts/toast_features.h
@@ -19,7 +19,6 @@
 BASE_DECLARE_FEATURE(kReadingListToast);
 BASE_DECLARE_FEATURE(kLensOverlayToast);
 BASE_DECLARE_FEATURE(kClearBrowsingDataToast);
-BASE_DECLARE_FEATURE(kPinnedTabToastOnClose);
 
 // Wrapper function used to check if a specific toast feature is enabled. Must
 // be used for toasts that are part of demo mode.
diff --git a/chrome/browser/ui/toasts/toast_service.cc b/chrome/browser/ui/toasts/toast_service.cc
index 72362bf..fdf3a0a 100644
--- a/chrome/browser/ui/toasts/toast_service.cc
+++ b/chrome/browser/ui/toasts/toast_service.cc
@@ -261,13 +261,11 @@
             .Build());
   }
 
-  if (toast_features::IsEnabled(toast_features::kPinnedTabToastOnClose)) {
-    toast_registry_->RegisterToast(
-        ToastId::kClosePinnedTab,
-        ToastSpecification::Builder(kKeepIcon, IDS_CLOSE_PINNED_TAB_TOAST_BODY)
-            .SetToastAsActionable()
-            .Build());
-  }
+  toast_registry_->RegisterToast(
+      ToastId::kClosePinnedTab,
+      ToastSpecification::Builder(kKeepIcon, IDS_CLOSE_PINNED_TAB_TOAST_BODY)
+          .SetToastAsActionable()
+          .Build());
 
   if (features::kGlicActorUiToast.Get()) {
     toast_registry_->RegisterToast(
diff --git a/chrome/browser/ui/toasts/toast_service_browsertest.cc b/chrome/browser/ui/toasts/toast_service_browsertest.cc
index e23e2ae..5cd13ccb 100644
--- a/chrome/browser/ui/toasts/toast_service_browsertest.cc
+++ b/chrome/browser/ui/toasts/toast_service_browsertest.cc
@@ -45,7 +45,6 @@
          {plus_addresses::features::kPlusAddressesEnabled, {}},
          {safe_browsing::kEsbAsASyncedSetting, {}},
          {data_sharing::features::kDataSharingFeature, {}},
-         {toast_features::kPinnedTabToastOnClose, {}},
          {features::kGlicActorUi, {{features::kGlicActorUiToastName, "true"}}}},
         /*disabled_features*/ {});
     InProcessBrowserTest::SetUp();
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 1e594bb5..55a439d0 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -44,12 +44,6 @@
 BASE_FEATURE(kFewerUpdateConfirmations, base::FEATURE_ENABLED_BY_DEFAULT);
 #endif
 
-#if !BUILDFLAG(IS_ANDROID)
-BASE_FEATURE(kDesktopNewTopAreaLayoutFeature,
-             "DesktopNewTopAreaLayout",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-#endif
-
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 
 BASE_FEATURE(kExtensionsCollapseMainMenu, base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index 3c4c8cc0..5ba42f8 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -34,11 +34,6 @@
 BASE_DECLARE_FEATURE(kFewerUpdateConfirmations);
 #endif
 
-#if !BUILDFLAG(IS_ANDROID)
-// Feature that manages the transition between old and new browser layout.
-BASE_DECLARE_FEATURE(kDesktopNewTopAreaLayoutFeature);
-#endif
-
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 
 // Controls how extensions show up in the main menu. When enabled, if the
diff --git a/chrome/browser/ui/views/autofill/popup/popup_view_views_browsertest.cc b/chrome/browser/ui/views/autofill/popup/popup_view_views_browsertest.cc
index bd890010..7ba3c93 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_view_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_view_views_browsertest.cc
@@ -24,7 +24,7 @@
 #include "components/autofill/core/common/aliases.h"
 #include "components/autofill/core/common/autofill_test_utils.h"
 #include "components/plus_addresses/core/browser/fake_plus_address_allocator.h"
-#include "components/plus_addresses/core/browser/plus_address_suggestion_generator.h"
+#include "components/plus_addresses/core/browser/plus_address_suggestion_helper.h"
 #include "components/plus_addresses/core/browser/settings/fake_plus_address_setting_service.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/test/browser_test.h"
@@ -591,11 +591,11 @@
 
   std::vector<Suggestion> GetPlusAddressSuggestion(
       const std::vector<std::string>& affiliated_plus_addresses) {
-    plus_addresses::PlusAddressSuggestionGenerator generator(
+    plus_addresses::PlusAddressSuggestionHelper helper(
         &setting_service(), &allocator(),
         url::Origin::Create(GURL("https://foo.bar")));
     FormData form = autofill::test::CreateTestSignupFormData();
-    return generator.GetSuggestions(
+    return helper.GetSuggestions(
         affiliated_plus_addresses,
         /*is_creation_enabled=*/true, form, form.fields()[0],
         /*form_field_type_groups=*/{}, PasswordFormClassification(),
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
index 7254942..cac3d4d 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.cc
@@ -49,6 +49,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
+#include "media/audio/audio_features.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -309,7 +310,7 @@
   return scroll_view;
 }
 
-int getHintId(bool is_system_audio_offered, bool is_window_audio_offered) {
+int GetHintId(bool is_system_audio_offered, bool is_window_audio_offered) {
   // We can never call this function if both screen and window audio are
   // offered.
   CHECK(!is_system_audio_offered || !is_window_audio_offered);
@@ -345,6 +346,37 @@
 #endif
 }
 
+// Returns the audio type for the window capture by taking into consideration
+// the `window_audio_type_requested_` and the system's capabilities.
+// Find more information at:
+// https://w3c.github.io/mediacapture-screen-share/#windowaudiopreferenceenum
+DesktopMediaID::AudioType GetWindowCaptureAudioType(
+    const DesktopMediaPicker::Params& params) {
+  if (!params.request_audio) {
+    return DesktopMediaID::AudioType::kNone;
+  }
+
+  if (params.window_audio_preference ==
+      blink::mojom::WindowAudioPreference::kExclude) {
+    return DesktopMediaID::AudioType::kNone;
+  }
+
+  if (params.window_audio_preference ==
+          blink::mojom::WindowAudioPreference::kWindow &&
+      media::IsApplicationAudioCaptureSupported()) {
+    return DesktopMediaID::AudioType::kApplication;
+  }
+
+  if (params.window_audio_preference ==
+          blink::mojom::WindowAudioPreference::kSystem &&
+      DesktopMediaPickerController::IsSystemAudioCaptureSupported(
+          params.request_source)) {
+    return DesktopMediaID::AudioType::kSystem;
+  }
+
+  return DesktopMediaID::AudioType::kNone;
+}
+
 }  // namespace
 
 bool DesktopMediaPickerDialogView::AudioSupported(
@@ -355,7 +387,8 @@
           request_source_);
     case DesktopMediaList::Type::kWindow:
       return DesktopMediaPickerController::IsSystemAudioCaptureSupported(
-          request_source_);
+                 request_source_) ||
+             media::IsApplicationAudioCaptureSupported();
     case DesktopMediaList::Type::kWebContents:
       return true;
     case DesktopMediaList::Type::kNone:
@@ -371,9 +404,10 @@
   // over the `categories_`, find the one with the relevant `type` and
   // return `category.audio_offered`.
   if (type == DesktopMediaList::Type::kScreen) {
-    return audio_requested_ && !exclude_system_audio_requested_;
+    return audio_requested_ && !screen_exclude_system_audio_requested_;
   } else if (type == DesktopMediaList::Type::kWindow) {
-    return audio_requested_ && !exclude_window_audio_requested_;
+    return audio_requested_ && (window_audio_type_requested_ !=
+                                blink::mojom::WindowAudioPreference::kExclude);
   } else {
     return audio_requested_;
   }
@@ -411,18 +445,12 @@
       request_source_(params.request_source),
       app_name_(params.app_name),
       audio_requested_(params.request_audio),
-      exclude_system_audio_requested_(params.exclude_system_audio),
-      exclude_window_audio_requested_(
-          params.window_audio_preference ==
-          blink::mojom::WindowAudioPreference::kExclude),
-      is_system_audio_offered_(audio_requested_ &&
+      screen_exclude_system_audio_requested_(params.exclude_system_audio),
+      is_screen_audio_offered_(audio_requested_ &&
                                !params.exclude_system_audio &&
                                AudioSupported(DesktopMediaList::Type::kScreen)),
-      is_window_audio_offered_(
-          audio_requested_ &&
-          params.window_audio_preference !=
-              blink::mojom::WindowAudioPreference::kExclude &&
-          AudioSupported(DesktopMediaList::Type::kWindow)),
+      window_audio_type_requested_(params.window_audio_preference),
+      window_audio_type_offered_(GetWindowCaptureAudioType(params)),
       // Only restrict_own_audio is used if both suppress_local_audio_playback
       // and restrict_own_audio are true. We need to make this choice since
       // there is no implementation for using both at the same time.
@@ -523,7 +551,7 @@
 
         std::unique_ptr<views::View> pane = SetupPane(
             DesktopMediaList::Type::kScreen, std::move(list_controller),
-            /*audio_offered=*/is_system_audio_offered_,
+            /*audio_offered=*/is_screen_audio_offered_,
             /*audio_checked=*/
             params.force_audio_checkboxes_to_default_checked ||
                 system_audio_capture_default_checked,
@@ -558,7 +586,7 @@
             views::ScrollView::ScrollBarMode::kDisabled);
         std::unique_ptr<views::View> pane = SetupPane(
             DesktopMediaList::Type::kWindow, std::move(list_controller),
-            /*audio_offered=*/is_window_audio_offered_,
+            /*audio_offered=*/IsWindowAudioOffered(),
             /*audio_checked=*/
             params.force_audio_checkboxes_to_default_checked ||
                 system_audio_capture_default_checked,
@@ -772,11 +800,30 @@
   reselect_button_ = SetExtraView(std::move(reselect_button));
 }
 
+int DesktopMediaPickerDialogView::GetLabelForWindowPaneAudioToggle() const {
+  switch (window_audio_type_offered_) {
+    case DesktopMediaID::AudioType::kNone:
+      return GetHintId(is_screen_audio_offered_,
+                       /*is_window_audio_offered=*/false);
+    case DesktopMediaID::AudioType::kApplication:
+      return IDS_DESKTOP_MEDIA_PICKER_ALSO_SHARE_APPLICATION_AUDIO;
+    case DesktopMediaID::AudioType::kSystem:
+      return GetLabelForShareSystemAudioToggle(suppress_local_audio_playback_,
+                                               restrict_own_audio_);
+  }
+  NOTREACHED();
+}
+
+bool DesktopMediaPickerDialogView::IsWindowAudioOffered() const {
+  return window_audio_type_offered_ !=
+         content::DesktopMediaID::AudioType::kNone;
+}
+
 std::u16string DesktopMediaPickerDialogView::GetLabelForAudioToggle(
     const DisplaySurfaceCategory& category) const {
   if (!category.audio_offered) {
     return l10n_util::GetStringUTF16(
-        getHintId(is_system_audio_offered_, is_window_audio_offered_));
+        GetHintId(is_screen_audio_offered_, IsWindowAudioOffered()));
   }
 
   switch (category.type) {
@@ -785,8 +832,9 @@
           suppress_local_audio_playback_, restrict_own_audio_));
     }
     case DesktopMediaList::Type::kWindow:
-      return l10n_util::GetStringUTF16(GetLabelForShareSystemAudioToggle(
-          suppress_local_audio_playback_, restrict_own_audio_));
+      // Check windowAudio preference, as we can select either window or system
+      // audio
+      return l10n_util::GetStringUTF16(GetLabelForWindowPaneAudioToggle());
     case DesktopMediaList::Type::kWebContents:
       return l10n_util::GetStringUTF16(
           IDS_DESKTOP_MEDIA_PICKER_ALSO_SHARE_TAB_AUDIO);
@@ -1046,6 +1094,10 @@
                               : GetSelectedController()->GetSelection().value();
   source.audio_share = IsAudioSharingApprovedByUser();
 
+  if (source.type == DesktopMediaID::Type::TYPE_WINDOW) {
+    source.window_audio_type = window_audio_type_offered_;
+  }
+
   if (request_source_ == RequestSource::kGetDisplayMedia) {
     RecordUmaSelection(capturer_global_id_, source, GetSelectedSourceListType(),
                        dialog_open_time_);
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
index cafa87e2..12fa56f 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views.h
@@ -174,6 +174,12 @@
   //   but no such sources were available.
   std::optional<int> CountSourcesOfType(DesktopMediaList::Type type);
 
+  int GetLabelForWindowPaneAudioToggle() const;
+
+  // Returns true if `window_audio_type_offered_` is not
+  // `content::DesktopMediaID::AudioType::AUDIO_TYPE_NONE`.
+  bool IsWindowAudioOffered() const;
+
 #if BUILDFLAG(IS_MAC)
   void OnPermissionUpdate(bool has_permission);
   void RecordPermissionInteractionUma() const;
@@ -184,12 +190,20 @@
   const DesktopMediaPicker::Params::RequestSource request_source_;
   const std::u16string app_name_;
   const bool audio_requested_;
-  const bool exclude_system_audio_requested_;  // JS-exposed as systemAudio.
-  const bool exclude_window_audio_requested_;  // JS-exposed as windowAudio.
-  const bool is_system_audio_offered_;
-  const bool is_window_audio_offered_;
-  const bool suppress_local_audio_playback_;  // Effective only if audio shared.
-  const bool restrict_own_audio_;             // Effective only if audio shared.
+  // JS-exposed as systemAudio.
+  const bool screen_exclude_system_audio_requested_;
+  // Indicates whether audio is currently being offered for screen captures.
+  const bool is_screen_audio_offered_;
+  // JS-exposed as windowAudio.
+  const blink::mojom::WindowAudioPreference window_audio_type_requested_;
+  // Indicates whether audio is currently being offered for window captures.
+  const content::DesktopMediaID::AudioType window_audio_type_offered_;
+  // If set to true, audio is captured, but is no longer played out over the
+  // user's local speakers. Effective only if audio shared.
+  const bool suppress_local_audio_playback_;
+  // If set to true, audio produced by Chromium should be excluded from the
+  // captured audio track. Effective only if audio shared.
+  const bool restrict_own_audio_;
   const content::GlobalRenderFrameHostId capturer_global_id_;
 
   raw_ptr<DesktopMediaPickerImpl> parent_;
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_test_api.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_test_api.cc
index 21a742eb..f36f6ec 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_test_api.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_test_api.cc
@@ -154,6 +154,18 @@
   return picker_->dialog_->IsAudioSharingApprovedByUser();
 }
 
+bool DesktopMediaPickerViewsTestApi::IsScreenAudioOffered() const {
+  return picker_->dialog_->is_screen_audio_offered_;
+}
+bool DesktopMediaPickerViewsTestApi::IsWindowAudioOffered() const {
+  return picker_->dialog_->window_audio_type_offered_ !=
+         content::DesktopMediaID::AudioType::kNone;
+}
+content::DesktopMediaID::AudioType
+DesktopMediaPickerViewsTestApi::GetWindowAudioType() const {
+  return picker_->dialog_->window_audio_type_offered_;
+}
+
 views::MdTextButton* DesktopMediaPickerViewsTestApi::GetReselectButton() {
   return picker_->dialog_->reselect_button_;
 }
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_test_api.h b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_test_api.h
index 7179b24..80ea0f4 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_test_api.h
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_test_api.h
@@ -47,6 +47,9 @@
   std::u16string_view GetAudioLabelText() const;
   void SetAudioSharingApprovedByUser(bool allow);
   bool IsAudioSharingApprovedByUser() const;
+  bool IsScreenAudioOffered() const;
+  bool IsWindowAudioOffered() const;
+  content::DesktopMediaID::AudioType GetWindowAudioType() const;
   views::MdTextButton* GetReselectButton();
 
   bool HasSourceAtIndex(size_t index) const;
diff --git a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_unittest.cc b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_unittest.cc
index 0031b5c..47fede7 100644
--- a/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_unittest.cc
+++ b/chrome/browser/ui/views/desktop_capture/desktop_media_picker_views_unittest.cc
@@ -129,8 +129,9 @@
   }
 
   virtual void MaybeCreatePickerViews() {
-    CreatePickerViews(/*request_audio=*/true, /*exclude_system_audio=*/false,
-                      /*exclude_window_audio=*/true);
+    CreatePickerViews(/*request_audio=*/true,
+                      /*screen_exclude_system_audio=*/false,
+                      blink::mojom::WindowAudioPreference::kExclude);
   }
 
   void TearDown() override {
@@ -143,8 +144,8 @@
 
   void CreatePickerViews(
       bool request_audio,
-      bool exclude_system_audio,
-      bool exclude_window_audio,
+      bool screen_exclude_system_audio,
+      blink::mojom::WindowAudioPreference window_audio_preference,
       blink::mojom::PreferredDisplaySurface preferred_display_surface =
           blink::mojom::PreferredDisplaySurface::NO_PREFERENCE) {
     widget_destroyed_waiter_.reset();
@@ -163,10 +164,8 @@
     picker_params.app_name = kAppName;
     picker_params.target_name = kAppName;
     picker_params.request_audio = request_audio;
-    picker_params.exclude_system_audio = exclude_system_audio;
-    picker_params.window_audio_preference =
-        exclude_window_audio ? blink::mojom::WindowAudioPreference::kExclude
-                             : blink::mojom::WindowAudioPreference::kSystem;
+    picker_params.exclude_system_audio = screen_exclude_system_audio;
+    picker_params.window_audio_preference = window_audio_preference;
     picker_params.preferred_display_surface = preferred_display_surface;
 
     std::vector<std::unique_ptr<DesktopMediaList>> source_lists;
@@ -598,32 +597,52 @@
 
 class DesktopMediaPickerViewsPerTypeAndAudioTest
     : public DesktopMediaPickerViewsTestBase,
-      public testing::WithParamInterface<
-          std::tuple<DesktopMediaList::Type, bool, bool, bool>> {
+      public testing::WithParamInterface<std::tuple<
+          DesktopMediaList::Type,
+          /*RequireAudio=*/bool,
+          /*SystemAudio=*/bool,
+          /*WindowAudioPreference=*/blink::mojom::WindowAudioPreference>> {
  public:
   DesktopMediaPickerViewsPerTypeAndAudioTest()
       : DesktopMediaPickerViewsTestBase(GetSourceTypes(/*new_order=*/true)) {}
   ~DesktopMediaPickerViewsPerTypeAndAudioTest() override = default;
 
+  void SetUp() override {
+#if BUILDFLAG(IS_WIN)
+    feature_list_.InitAndEnableFeature(features::kApplicationAudioCaptureWin);
+#endif  // BUILDFLAG(IS_WIN)
+    DesktopMediaPickerViewsTestBase::SetUp();
+  }
+
   void MaybeCreatePickerViews() override {
-    CreatePickerViews(RequireAudio(), SystemAudio(), WindowAudio());
+    CreatePickerViews(RequireAudio(), SystemAudio(), WindowAudioPreference());
   }
 
   DesktopMediaList::Type Type() const { return std::get<0>(GetParam()); }
   bool RequireAudio() const { return std::get<1>(GetParam()); }
   bool SystemAudio() const { return std::get<2>(GetParam()); }
-  bool WindowAudio() const { return std::get<3>(GetParam()); }
+  blink::mojom::WindowAudioPreference WindowAudioPreference() const {
+    return std::get<3>(GetParam());
+  }
+
+ private:
+#if BUILDFLAG(IS_WIN)
+  base::test::ScopedFeatureList feature_list_;
+#endif  // BUILDFLAG(IS_WIN)
 };
 
 INSTANTIATE_TEST_SUITE_P(
     ,
     DesktopMediaPickerViewsPerTypeAndAudioTest,
-    testing::Combine(testing::Values(DesktopMediaList::Type::kWebContents,
-                                     DesktopMediaList::Type::kWindow,
-                                     DesktopMediaList::Type::kScreen),
-                     testing::Bool(),
-                     testing::Bool(),
-                     testing::Bool()));
+    testing::Combine(
+        testing::Values(DesktopMediaList::Type::kWebContents,
+                        DesktopMediaList::Type::kWindow,
+                        DesktopMediaList::Type::kScreen),
+        testing::Bool(),
+        testing::Bool(),
+        testing::Values(blink::mojom::WindowAudioPreference::kExclude,
+                        blink::mojom::WindowAudioPreference::kSystem,
+                        blink::mojom::WindowAudioPreference::kWindow)));
 
 TEST_P(DesktopMediaPickerViewsPerTypeAndAudioTest, AcceptSpecific) {
   DesktopMediaID fake_id(AsDesktopMediaIdType(Type()), 333);
@@ -632,6 +651,10 @@
   if (RequireAudio() &&
       AsDesktopMediaIdType(Type()) == DesktopMediaID::TYPE_WEB_CONTENTS) {
     fake_id.audio_share = true;
+  } else if (AsDesktopMediaIdType(Type()) == DesktopMediaID::TYPE_WINDOW) {
+    // For Window captures, the `window_audio_type` will be set based on
+    // platform support.
+    fake_id.window_audio_type = test_api_.GetWindowAudioType();
   }
   media_lists_[Type()]->AddSourceByFullMediaID(fake_id);
 
@@ -667,7 +690,7 @@
 TEST_F(DesktopMediaPickerViewsSystemAudioTest,
        SystemAudioCheckboxVisibleIfExcludeSystemAudioNotSpecified) {
   CreatePickerViews(/*request_audio=*/true, /*exclude_system_audio=*/false,
-                    /*exclude_window_audio=*/false);
+                    blink::mojom::WindowAudioPreference::kSystem);
 
   test_api_.SelectTabForSourceType(DesktopMediaList::Type::kScreen);
 
@@ -687,7 +710,7 @@
 TEST_F(DesktopMediaPickerViewsSystemAudioTest,
        SystemAudioCheckboxInvisibleIfExcludeSystemAudioSpecified) {
   CreatePickerViews(/*request_audio=*/true, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   test_api_.SelectTabForSourceType(DesktopMediaList::Type::kScreen);
 
@@ -715,7 +738,7 @@
 TEST_F(DesktopMediaPickerViewsSystemAudioTest,
        IfAudioNotRequestedThenExcludeSystemAudioHasNoEffect) {
   CreatePickerViews(/*request_audio=*/false, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   test_api_.SelectTabForSourceType(DesktopMediaList::Type::kScreen);
 
@@ -735,7 +758,7 @@
 TEST_F(DesktopMediaPickerViewsSystemAudioTest,
        CorrectHintsIfSystemAudioIsExcluded) {
   CreatePickerViews(/*request_audio=*/true, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/false);
+                    blink::mojom::WindowAudioPreference::kSystem);
 
   test_api_.SelectTabForSourceType(DesktopMediaList::Type::kScreen);
 
@@ -753,7 +776,7 @@
 TEST_F(DesktopMediaPickerViewsSystemAudioTest,
        CorrectHintsIfWindowAudioIsExcluded) {
   CreatePickerViews(/*request_audio=*/true, /*exclude_system_audio=*/false,
-                    /*exclude_window_audio=*/true);
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   test_api_.SelectTabForSourceType(DesktopMediaList::Type::kWindow);
 
@@ -768,6 +791,152 @@
                     : IDS_DESKTOP_MEDIA_PICKER_AUDIO_SHARE_HINT_TAB));
 }
 
+#if BUILDFLAG(IS_WIN)
+// Verifies the conditions that make the media picker audio checkbox enabled for
+// each type of pane (Tab, Window, Screen) when application audio capture is
+// available/unavailable. Also checks that the checkbox string is correct for
+// each type of pane. Application audio capture is currently only supported on
+// Windows.
+class DesktopMediaPickerViewsApplicationAudioTest
+    : public DesktopMediaPickerViewsTestBase,
+      public testing::WithParamInterface<
+          std::tuple<blink::mojom::WindowAudioPreference,
+                     /*is_application_audio_capture_supported=*/bool,
+                     /*request_audio=*/bool,
+                     /*screen_exclude_system_audio=*/bool>> {
+ public:
+  DesktopMediaPickerViewsApplicationAudioTest()
+      : DesktopMediaPickerViewsTestBase(GetSourceTypes(/*new_order=*/false)) {}
+  ~DesktopMediaPickerViewsApplicationAudioTest() override = default;
+
+  void SetUp() override {
+    if (ShouldEnableApplicationAudioCapture()) {
+      feature_list_.InitAndEnableFeature(features::kApplicationAudioCaptureWin);
+    } else {
+      feature_list_.InitAndDisableFeature(
+          features::kApplicationAudioCaptureWin);
+    }
+    DesktopMediaPickerViewsTestBase::SetUp();
+  }
+
+  void MaybeCreatePickerViews() override {
+    // CreatePickerViews() called  directly from tests.
+  }
+
+  blink::mojom::WindowAudioPreference WindowAudioPreference() const {
+    return std::get<0>(GetParam());
+  }
+
+  bool ShouldEnableApplicationAudioCapture() const {
+    return std::get<1>(GetParam());
+  }
+
+  bool RequestAudio() const { return std::get<2>(GetParam()); }
+
+  // Returns true if the `systemAudio` parameter passed to getisplayMedia was
+  // set to "exclude".
+  bool ScreenExcludeSystemAudio() const { return std::get<3>(GetParam()); }
+
+  // Returns true if the screen tab should offer audio sharing.
+  bool ShouldOfferScreenAudio() const {
+    return RequestAudio() && !ScreenExcludeSystemAudio();
+  }
+
+  // Returns true if the window tab should offer audio sharing.
+  bool ShouldOfferWindowAudio() const {
+    return RequestAudio() &&
+           WindowAudioPreference() !=
+               blink::mojom::WindowAudioPreference::kExclude &&
+           (((media::IsApplicationAudioCaptureSupported() &&
+              WindowAudioPreference() ==
+                  blink::mojom::WindowAudioPreference::kWindow)) ||
+            (WindowAudioPreference() ==
+             blink::mojom::WindowAudioPreference::kSystem));
+  }
+
+  // Returns the expected label for the screen pane's audio toggle of the
+  // getDisplayMedia picker UI.
+  std::u16string GetExpectedScreenAudioLabel() const {
+    if (!RequestAudio()) {
+      return std::u16string();
+    }
+
+    if (ShouldOfferScreenAudio()) {
+      return l10n_util::GetStringUTF16(
+          IDS_DESKTOP_MEDIA_PICKER_ALSO_SHARE_SYSTEM_AUDIO);
+    }
+
+    if (ShouldOfferWindowAudio()) {
+      return l10n_util::GetStringUTF16(
+          IDS_DESKTOP_MEDIA_PICKER_AUDIO_SHARE_HINT_TAB_OR_WINDOW);
+    }
+
+    return l10n_util::GetStringUTF16(
+        IDS_DESKTOP_MEDIA_PICKER_AUDIO_SHARE_HINT_TAB);
+  }
+
+  // Returns the expected label for the window pane's audio toggle of the
+  // getDisplayMedia picker UI.
+  std::u16string GetExpectedWindowAudioLabel() const {
+    if (!RequestAudio()) {
+      return std::u16string();
+    }
+
+    if (ShouldOfferWindowAudio()) {
+      if (WindowAudioPreference() ==
+              blink::mojom::WindowAudioPreference::kWindow &&
+          media::IsApplicationAudioCaptureSupported()) {
+        return l10n_util::GetStringUTF16(
+            IDS_DESKTOP_MEDIA_PICKER_ALSO_SHARE_APPLICATION_AUDIO);
+      }
+
+      return l10n_util::GetStringUTF16(
+          IDS_DESKTOP_MEDIA_PICKER_ALSO_SHARE_SYSTEM_AUDIO);
+    }
+
+    if (ShouldOfferScreenAudio()) {
+      return l10n_util::GetStringUTF16(
+          IDS_DESKTOP_MEDIA_PICKER_AUDIO_SHARE_HINT_TAB_OR_SCREEN);
+    }
+
+    return l10n_util::GetStringUTF16(
+        IDS_DESKTOP_MEDIA_PICKER_AUDIO_SHARE_HINT_TAB);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    DesktopMediaPickerViewsApplicationAudioTest,
+    testing::Combine(
+        testing::Values(blink::mojom::WindowAudioPreference::kExclude,
+                        blink::mojom::WindowAudioPreference::kSystem,
+                        blink::mojom::WindowAudioPreference::kWindow),
+        testing::Bool(),
+        testing::Bool(),
+        testing::Bool()));
+
+TEST_P(DesktopMediaPickerViewsApplicationAudioTest, AudioCheckbox) {
+  CreatePickerViews(RequestAudio(), ScreenExcludeSystemAudio(),
+                    WindowAudioPreference());
+
+  test_api_.SelectTabForSourceType(DesktopMediaList::Type::kScreen);
+  EXPECT_EQ(test_api_.HasAudioShareControl(), ShouldOfferScreenAudio());
+  EXPECT_EQ(test_api_.IsScreenAudioOffered(), ShouldOfferScreenAudio());
+  EXPECT_EQ(test_api_.GetAudioLabelText(), GetExpectedScreenAudioLabel());
+
+  test_api_.SelectTabForSourceType(DesktopMediaList::Type::kWindow);
+  EXPECT_EQ(test_api_.HasAudioShareControl(), ShouldOfferWindowAudio());
+  EXPECT_EQ(test_api_.IsWindowAudioOffered(), ShouldOfferWindowAudio());
+  EXPECT_EQ(test_api_.GetAudioLabelText(), GetExpectedWindowAudioLabel());
+
+  test_api_.SelectTabForSourceType(DesktopMediaList::Type::kWebContents);
+  EXPECT_EQ(test_api_.HasAudioShareControl(), RequestAudio());
+}
+#endif  // BUILDFLAG(IS_WIN)
+
 // Creates a single pane DesktopMediaPickerImpl that only has a tab list.
 class DesktopMediaPickerViewsSingleTabPaneTest
     : public DesktopMediaPickerViewsTestBase {
@@ -918,8 +1087,10 @@
       : DesktopMediaPickerViewsTestBase(GetSourceTypes(NewOrder())) {}
 
   void MaybeCreatePickerViews() override {
-    CreatePickerViews(/*request_audio=*/true, /*exclude_system_audio=*/false,
-                      /*exclude_window_audio=*/true, PreferredDisplaySurface());
+    CreatePickerViews(/*request_audio=*/true,
+                      /*screen_exclude_system_audio=*/false,
+                      blink::mojom::WindowAudioPreference::kExclude,
+                      PreferredDisplaySurface());
   }
 
   bool NewOrder() const { return std::get<0>(GetParam()); }
@@ -1026,8 +1197,9 @@
   SetSourceTypes(
       {DesktopMediaList::Type::kWebContents},
       {DesktopMediaList::Type::kScreen, DesktopMediaList::Type::kWindow});
-  CreatePickerViews(/*request_audio=*/false, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+  CreatePickerViews(/*request_audio=*/false,
+                    /*screen_exclude_system_audio=*/true,
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   test_api_.SelectTabForSourceType(DesktopMediaList::Type::kWebContents);
   EXPECT_FALSE(media_lists_[DesktopMediaList::Type::kScreen]->is_focused());
@@ -1050,8 +1222,9 @@
   SetSourceTypes(
       {DesktopMediaList::Type::kWebContents},
       {DesktopMediaList::Type::kScreen, DesktopMediaList::Type::kWindow});
-  CreatePickerViews(/*request_audio=*/false, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+  CreatePickerViews(/*request_audio=*/false,
+                    /*screen_exclude_system_audio=*/true,
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   // Add the one entry that is expected for a delegated source list and switch
   // to it. Note that since this is a delegated source, we must select its pane
@@ -1074,8 +1247,9 @@
   SetSourceTypes(
       {DesktopMediaList::Type::kWebContents},
       {DesktopMediaList::Type::kScreen, DesktopMediaList::Type::kWindow});
-  CreatePickerViews(/*request_audio=*/false, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+  CreatePickerViews(/*request_audio=*/false,
+                    /*screen_exclude_system_audio=*/true,
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   // Add the one entry that is expected for a delegated source list and switch
   // to it. Note that since this is a delegated source, we must select its pane
@@ -1101,8 +1275,9 @@
 // delegated source list is dismissed that it finishes without a selection.
 TEST_F(DelegatedSourceListTest, SinglePaneReject) {
   SetSourceTypes({}, {DesktopMediaList::Type::kScreen});
-  CreatePickerViews(/*request_audio=*/false, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+  CreatePickerViews(/*request_audio=*/false,
+                    /*screen_exclude_system_audio=*/true,
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   media_lists_[DesktopMediaList::Type::kScreen]
       ->OnDelegatedSourceListDismissed();
@@ -1120,8 +1295,9 @@
   // isn't one of them.
   SetSourceTypes(
       {}, {DesktopMediaList::Type::kScreen, DesktopMediaList::Type::kWindow});
-  CreatePickerViews(/*request_audio=*/false, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+  CreatePickerViews(/*request_audio=*/false,
+                    /*screen_exclude_system_audio=*/true,
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   media_lists_[DesktopMediaList::Type::kScreen]
       ->OnDelegatedSourceListDismissed();
@@ -1139,8 +1315,9 @@
   // one other type.
   SetSourceTypes({DesktopMediaList::Type::kWebContents},
                  {DesktopMediaList::Type::kScreen});
-  CreatePickerViews(/*request_audio=*/false, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+  CreatePickerViews(/*request_audio=*/false,
+                    /*screen_exclude_system_audio=*/true,
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   // Switch to the screen pane and simulate the user dismissing the native
   // picker.
@@ -1162,8 +1339,9 @@
   SetSourceTypes(
       {DesktopMediaList::Type::kWebContents},
       {DesktopMediaList::Type::kScreen, DesktopMediaList::Type::kWindow});
-  CreatePickerViews(/*request_audio=*/false, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+  CreatePickerViews(/*request_audio=*/false,
+                    /*screen_exclude_system_audio=*/true,
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   // Ensure that we don't have a reselect button for the non-delegated type.
   test_api_.SelectTabForSourceType(DesktopMediaList::Type::kWebContents);
@@ -1187,8 +1365,9 @@
   // one other type.
   SetSourceTypes({DesktopMediaList::Type::kWebContents},
                  {DesktopMediaList::Type::kScreen});
-  CreatePickerViews(/*request_audio=*/false, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+  CreatePickerViews(/*request_audio=*/false,
+                    /*screen_exclude_system_audio=*/true,
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   // Switch to the screen pane, dismiss it, then validate that we're back on
   // the WebContents pane.
@@ -1207,8 +1386,9 @@
   // Ensure that we have the (Fallback) WebContents type and a different type
   SetSourceTypes({DesktopMediaList::Type::kWebContents},
                  {DesktopMediaList::Type::kScreen});
-  CreatePickerViews(/*request_audio=*/false, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+  CreatePickerViews(/*request_audio=*/false,
+                    /*screen_exclude_system_audio=*/true,
+                    blink::mojom::WindowAudioPreference::kExclude);
   const auto web_contents_source_type =
       AsDesktopMediaIdType(DesktopMediaList::Type::kWebContents);
 
@@ -1242,8 +1422,9 @@
   SetSourceTypes(
       {DesktopMediaList::Type::kWebContents},
       {DesktopMediaList::Type::kScreen, DesktopMediaList::Type::kWindow});
-  CreatePickerViews(/*request_audio=*/false, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+  CreatePickerViews(/*request_audio=*/false,
+                    /*screen_exclude_system_audio=*/true,
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   // Ensure that we don't have a reselect button for the non-delegated type.
   test_api_.SelectTabForSourceType(DesktopMediaList::Type::kWebContents);
@@ -1274,8 +1455,9 @@
   SetSourceTypes(
       {DesktopMediaList::Type::kWebContents},
       {DesktopMediaList::Type::kScreen, DesktopMediaList::Type::kWindow});
-  CreatePickerViews(/*request_audio=*/false, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+  CreatePickerViews(/*request_audio=*/false,
+                    /*screen_exclude_system_audio=*/true,
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   // Ensure that we do have a reselect button for the screen delegated type, and
   // that it is not enabled by default.
@@ -1313,8 +1495,9 @@
   SetSourceTypes(
       {DesktopMediaList::Type::kWebContents},
       {DesktopMediaList::Type::kScreen, DesktopMediaList::Type::kWindow});
-  CreatePickerViews(/*request_audio=*/false, /*exclude_system_audio=*/true,
-                    /*exclude_window_audio=*/true);
+  CreatePickerViews(/*request_audio=*/false,
+                    /*screen_exclude_system_audio=*/true,
+                    blink::mojom::WindowAudioPreference::kExclude);
 
   // ClearSourceListSelection should not have been called on either list yet.
   EXPECT_EQ(0, media_lists_[DesktopMediaList::Type::kScreen]
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_chromeos_browsertest.cc b/chrome/browser/ui/views/frame/browser_frame_view_chromeos_browsertest.cc
index 92e02995..d65b314 100644
--- a/chrome/browser/ui/views/frame/browser_frame_view_chromeos_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_view_chromeos_browsertest.cc
@@ -271,10 +271,6 @@
 
 IN_PROC_BROWSER_TEST_F(BrowserFrameViewChromeOSTouchTestWithWebUiTabStrip,
                        TabletSplitViewNonClientHitTest) {
-  if (!IsSnapWindowSupported()) {
-    GTEST_SKIP() << "Ash is too old.";
-  }
-
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
   BrowserFrameViewChromeOS* frame_view = GetFrameViewChromeOS(browser_view);
   views::Widget* widget = browser_view->GetWidget();
@@ -294,10 +290,6 @@
 
 IN_PROC_BROWSER_TEST_F(BrowserFrameViewChromeOSTouchTestWithWebUiTabStrip,
                        TabletSplitViewSwipeDownFromEdgeOpensWebUiTabStrip) {
-  if (!IsSnapWindowSupported()) {
-    GTEST_SKIP() << "Ash is too old.";
-  }
-
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
   BrowserFrameViewChromeOS* frame_view = GetFrameViewChromeOS(browser_view);
   const int expect_y =
@@ -972,10 +964,6 @@
 // tablet mode.
 IN_PROC_BROWSER_TEST_P(BrowserFrameViewChromeOSTest,
                        BrowserHeaderVisibilityInTabletModeTest) {
-  if (!IsSnapWindowSupported()) {
-    GTEST_SKIP() << "Ash is too old.";
-  }
-
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
   views::Widget* widget = browser_view->GetWidget();
   BrowserFrameViewChromeOS* frame_view = GetFrameViewChromeOS(browser_view);
@@ -1309,10 +1297,6 @@
 
 IN_PROC_BROWSER_TEST_P(FloatBrowserFrameViewChromeOSTest,
                        BrowserHeaderVisibilityInTabletModeTest) {
-  if (!IsSnapWindowSupported()) {
-    GTEST_SKIP() << "Ash is too old.";
-  }
-
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
   BrowserFrameViewChromeOS* frame_view = GetFrameViewChromeOS(browser_view);
 
@@ -1339,10 +1323,6 @@
 // in tablet mode.
 IN_PROC_BROWSER_TEST_P(FloatBrowserFrameViewChromeOSTest,
                        BrowserAppHeaderVisibilityInTabletModeTest) {
-  if (!IsSnapWindowSupported()) {
-    GTEST_SKIP() << "Ash is too old.";
-  }
-
   Browser* browser2 =
       CreateBrowserForApp("test_browser_app", browser()->profile());
   BrowserView* browser_view2 = BrowserView::GetBrowserViewForBrowser(browser2);
@@ -1525,10 +1505,6 @@
 
 IN_PROC_BROWSER_TEST_P(LockedFullscreenBrowserFrameViewChromeOSTest,
                        ToggleTabletModeWhenNotLockedForOnTask) {
-  if (!IsIsShelfVisibleSupported()) {
-    GTEST_SKIP() << "Ash is too old.";
-  }
-
   browser()->SetLockedForOnTask(false);
   BrowserView* const browser_view =
       BrowserView::GetBrowserViewForBrowser(browser());
@@ -1569,10 +1545,6 @@
 
 IN_PROC_BROWSER_TEST_P(LockedFullscreenBrowserFrameViewChromeOSTest,
                        ToggleTabletModeWhenLockedForOnTask) {
-  if (!IsIsShelfVisibleSupported()) {
-    GTEST_SKIP() << "Ash is too old.";
-  }
-
   browser()->SetLockedForOnTask(true);
   BrowserView* const browser_view =
       BrowserView::GetBrowserViewForBrowser(browser());
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 9cacfda..37972c8 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -886,7 +886,7 @@
  private:
   // Do not friend BrowserViewLayout. Use the BrowserViewLayoutDelegate
   // interface to keep these two classes decoupled and testable.
-  friend class BrowserViewLayoutDelegateImplNew;
+  friend class BrowserViewLayoutDelegateImpl;
   friend class BrowserViewLayoutDelegateImplOld;
   friend class BrowserViewLayoutDelegateImplBrowsertest;
   friend class TopControlsSlideControllerTest;
diff --git a/chrome/browser/ui/views/frame/browser_view_layout_delegate_impl.cc b/chrome/browser/ui/views/frame/browser_view_layout_delegate_impl.cc
index 96f32b6d..74e1638 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout_delegate_impl.cc
+++ b/chrome/browser/ui/views/frame/browser_view_layout_delegate_impl.cc
@@ -20,11 +20,19 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/view.h"
 
+namespace {
+
+// Feature that manages the transition between old and current browser layout
+// delegate. This feature is on by default and provided only as a killswitch.
+BASE_FEATURE(kDesktopNewTopAreaLayoutFeature, base::FEATURE_ENABLED_BY_DEFAULT);
+
+}  // namespace
+
 // static
 std::unique_ptr<BrowserViewLayoutDelegate>
 BrowserViewLayoutDelegateImplBase::CreateDelegate(BrowserView& browser_view) {
-  if (base::FeatureList::IsEnabled(features::kDesktopNewTopAreaLayoutFeature)) {
-    return std::make_unique<BrowserViewLayoutDelegateImplNew>(browser_view);
+  if (base::FeatureList::IsEnabled(kDesktopNewTopAreaLayoutFeature)) {
+    return std::make_unique<BrowserViewLayoutDelegateImpl>(browser_view);
   }
   return std::make_unique<BrowserViewLayoutDelegateImplOld>(browser_view);
 }
@@ -225,14 +233,13 @@
   return gfx::ToEnclosingRect(bounds_f);
 }
 
-BrowserViewLayoutDelegateImplNew::BrowserViewLayoutDelegateImplNew(
+BrowserViewLayoutDelegateImpl::BrowserViewLayoutDelegateImpl(
     BrowserView& browser_view)
     : BrowserViewLayoutDelegateImplBase(browser_view) {}
-BrowserViewLayoutDelegateImplNew::~BrowserViewLayoutDelegateImplNew() = default;
+BrowserViewLayoutDelegateImpl::~BrowserViewLayoutDelegateImpl() = default;
 
 gfx::Rect
-BrowserViewLayoutDelegateImplNew::GetBoundsForTabStripRegionInBrowserView()
-    const {
+BrowserViewLayoutDelegateImpl::GetBoundsForTabStripRegionInBrowserView() const {
   const gfx::Size tabstrip_minimum_size =
       browser_view().tab_strip_view()->GetMinimumSize();
   const auto layout = GetFrameView()->GetBrowserLayoutParams();
@@ -260,7 +267,7 @@
 }
 
 gfx::Rect
-BrowserViewLayoutDelegateImplNew::GetBoundsForToolbarInVerticalTabBrowserView()
+BrowserViewLayoutDelegateImpl::GetBoundsForToolbarInVerticalTabBrowserView()
     const {
   const gfx::Size toolbar_preferred_size =
       browser_view().toolbar()->GetPreferredSize();
@@ -286,7 +293,7 @@
 }
 
 gfx::Rect
-BrowserViewLayoutDelegateImplNew::GetBoundsForWebAppFrameToolbarInBrowserView()
+BrowserViewLayoutDelegateImpl::GetBoundsForWebAppFrameToolbarInBrowserView()
     const {
   if (!GetFrameView()->ShouldShowWebAppFrameToolbar()) {
     return gfx::Rect();
diff --git a/chrome/browser/ui/views/frame/browser_view_layout_delegate_impl.h b/chrome/browser/ui/views/frame/browser_view_layout_delegate_impl.h
index 8aa7c4c3..7024047 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout_delegate_impl.h
+++ b/chrome/browser/ui/views/frame/browser_view_layout_delegate_impl.h
@@ -77,11 +77,10 @@
 
 // The new implementation of the layout delegate; uses new BrowserLayoutParams
 // API.
-class BrowserViewLayoutDelegateImplNew
-    : public BrowserViewLayoutDelegateImplBase {
+class BrowserViewLayoutDelegateImpl : public BrowserViewLayoutDelegateImplBase {
  public:
-  explicit BrowserViewLayoutDelegateImplNew(BrowserView& browser_view);
-  ~BrowserViewLayoutDelegateImplNew() override;
+  explicit BrowserViewLayoutDelegateImpl(BrowserView& browser_view);
+  ~BrowserViewLayoutDelegateImpl() override;
 
   gfx::Rect GetBoundsForTabStripRegionInBrowserView() const override;
   gfx::Rect GetBoundsForToolbarInVerticalTabBrowserView() const override;
diff --git a/chrome/browser/ui/views/frame/browser_view_layout_delegate_impl_browsertest.cc b/chrome/browser/ui/views/frame/browser_view_layout_delegate_impl_browsertest.cc
index 7610dabe..1086bc2 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout_delegate_impl_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_view_layout_delegate_impl_browsertest.cc
@@ -157,7 +157,7 @@
     auto* const layout = browser_view->GetBrowserViewLayout();
     if (use_new_delegate) {
       layout->SetDelegateForTesting(
-          std::make_unique<BrowserViewLayoutDelegateImplNew>(*browser_view));
+          std::make_unique<BrowserViewLayoutDelegateImpl>(*browser_view));
     } else {
       layout->SetDelegateForTesting(
           std::make_unique<BrowserViewLayoutDelegateImplOld>(*browser_view));
diff --git a/chrome/browser/ui/views/frame/immersive_mode_browser_view_test.cc b/chrome/browser/ui/views/frame/immersive_mode_browser_view_test.cc
index 04f99d4..b3520de 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_browser_view_test.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_browser_view_test.cc
@@ -286,10 +286,6 @@
 // hidden, and the fullscreen control popup doesn't show up).
 IN_PROC_BROWSER_TEST_P(ImmersiveModeBrowserViewTest,
                        RegularToLockedFullscreenDisablesImmersive) {
-  if (!IsIsShelfVisibleSupported()) {
-    GTEST_SKIP() << "Ash is too old.";
-  }
-
   EnterImmersiveFullscreenMode(browser());
 
   // Set locked fullscreen state.
@@ -317,10 +313,6 @@
 // fullscreen control popup doesn't show up).
 IN_PROC_BROWSER_TEST_P(ImmersiveModeBrowserViewTest,
                        LockedFullscreenDisablesImmersive) {
-  if (!IsIsShelfVisibleSupported()) {
-    GTEST_SKIP() << "Ash is too old.";
-  }
-
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
   EXPECT_FALSE(browser_view->GetWidget()->IsFullscreen());
 
@@ -346,10 +338,6 @@
 // Test the shelf visibility affected by entering and exiting tab fullscreen and
 // immersive fullscreen.
 IN_PROC_BROWSER_TEST_P(ImmersiveModeBrowserViewTest, TabAndBrowserFullscreen) {
-  if (!IsIsShelfVisibleSupported()) {
-    GTEST_SKIP() << "Ash is too old.";
-  }
-
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
 
   ASSERT_TRUE(
diff --git a/chrome/browser/ui/views/notifications/request_pin_view_chromeos.h b/chrome/browser/ui/views/notifications/request_pin_view_chromeos.h
index 66871237aa..1676d0f 100644
--- a/chrome/browser/ui/views/notifications/request_pin_view_chromeos.h
+++ b/chrome/browser/ui/views/notifications/request_pin_view_chromeos.h
@@ -11,7 +11,6 @@
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/certificate_provider/security_token_pin_dialog_host.h"
 #include "chromeos/components/security_token_pin/constants.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/mojom/dialog_button.mojom.h"
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_presenter.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_presenter.cc
index 23acd1c..d6cfabea 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_presenter.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_presenter.cc
@@ -77,6 +77,18 @@
     // On Show(), the widget height can not be 0 or else the compositor thinks
     // the webview is hidden and will not calculate its preferred size.
     SetWidgetContentHeight(1);
+
+    // Manually set zoom level, since any zooming is undesirable in the omnibox.
+    auto* zoom_controller =
+        zoom::ZoomController::FromWebContents(GetWebContents());
+    if (!zoom_controller) {
+      // Create ZoomController manually, if not already exists, because it is
+      // not automatically created when the WebUI has not been opened in a tab.
+      zoom_controller =
+          zoom::ZoomController::CreateForWebContents(GetWebContents());
+    }
+    zoom_controller->SetZoomMode(zoom::ZoomController::ZOOM_MODE_ISOLATED);
+    zoom_controller->SetZoomLevel(0);
   }
 }
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.h b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
index 97724ab8..5cc15c4 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
@@ -65,6 +65,17 @@
       public views::TextfieldController,
       public ui::CompositorObserver,
       public TemplateURLServiceObserver {
+  // TODO(crbug.com/392015004): Remove this macro once it gets fixed.
+  //
+  // Both `OmniboxView` and `views::Textfield` (*1) have the
+  // `ADVANCED_MEMORY_SAFETY_CHECKS` macro, hence there is ambiguity about which
+  // `operator new` should be used (although the two `operator new` are
+  // eventually equivalent). Choose `OmniboxView` with no deep reason.
+  //
+  // (*1) Note that `views::Textfield` inherits from `views::View`, which has
+  // the `ADVANCED_MEMORY_SAFETY_CHECKS` macro.
+  INHERIT_MEMORY_SAFETY_CHECKS(OmniboxView);
+
   METADATA_HEADER(OmniboxViewViews, views::Textfield)
 
  public:
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
index 3b302b6..25aa2f5 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
@@ -22,6 +22,7 @@
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/metrics/user_action_tester.h"
+#include "base/test/run_until.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/with_feature_override.h"
 #include "base/threading/thread_restrictions.h"
@@ -2695,12 +2696,7 @@
  protected:
   ProfileMenuSigninAccessPointTest()
       : delegate_auto_reset_(signin_ui_util::SetSigninUiDelegateForTesting(
-            &mock_signin_ui_delegate_)) {
-    // TODO(crbug.com/448053484): Investigate why enabling the feature leads to
-    // test flakiness.
-    feature_list_.InitAndDisableFeature(
-        syncer::kReplaceSyncPromosWithSignInPromos);
-  }
+            &mock_signin_ui_delegate_)) {}
 
   void OpenProfileMenuFromCoordinator(
       std::optional<signin_metrics::AccessPoint> explicit_access_point =
@@ -2708,6 +2704,8 @@
     auto* coordinator = browser()->GetFeatures().profile_menu_coordinator();
     ASSERT_TRUE(coordinator);
     coordinator->Show(/*is_source_accelerator=*/false, explicit_access_point);
+    ASSERT_TRUE(base::test::RunUntil(
+        [coordinator]() { return coordinator->IsShowing(); }));
     ASSERT_NO_FATAL_FAILURE(
         WaitForMenuToBeActive(coordinator->GetProfileMenuViewBaseForTesting()));
   }
@@ -2718,6 +2716,7 @@
     ProfileMenuViewBase* profile_menu_view =
         coordinator->GetProfileMenuViewBaseForTesting();
     ASSERT_TRUE(profile_menu_view);
+    profile_menu_view->GetFocusManager()->ClearFocus();
     profile_menu_view->GetFocusManager()->AdvanceFocus(/*reverse=*/false);
     views::View* focused_view =
         profile_menu_view->GetFocusManager()->GetFocusedView();
@@ -2731,8 +2730,6 @@
 
  private:
   base::AutoReset<signin_ui_util::SigninUiDelegate*> delegate_auto_reset_;
-
-  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(ProfileMenuSigninAccessPointTest,
@@ -2741,11 +2738,6 @@
   const signin_metrics::AccessPoint default_access_point =
       signin_metrics::AccessPoint::kAvatarBubbleSignIn;
   ASSERT_NO_FATAL_FAILURE(OpenProfileMenuFromCoordinator());
-  // `Signin.SyncOptIn.Offered` should be recorded if the sync opt-in is
-  // offered from the profile menu.
-  histogram_tester.ExpectUniqueSample("Signin.SyncOptIn.Offered",
-                                      default_access_point,
-                                      /*expected_bucket_count=*/1);
   // `Signin.SignIn.Offered` should NOT be recorded if the sign-in is not
   // directly offered from the profile menu.
   histogram_tester.ExpectUniqueSample("Signin.SignIn.Offered",
@@ -2753,11 +2745,34 @@
                                       /*expected_bucket_count=*/0);
   if (base::FeatureList::IsEnabled(
           syncer::kReplaceSyncPromosWithSignInPromos)) {
+    // `Signin.SyncOptIn.Offered` should be not recorded if
+    // `syncer::kReplaceSyncPromosWithSignInPromos` is enabled. Instead,
+    // `Signin.HistorySyncOptIn.Offered` should be.
+    histogram_tester.ExpectTotalCount("Signin.SyncOptIn.Offered",
+                                      /*expected_count=*/0);
+    histogram_tester.ExpectUniqueSample("Signin.HistorySyncOptIn.Offered",
+                                        default_access_point,
+                                        /*expected_bucket_count=*/1);
+
     EXPECT_CALL(
         mock_signin_ui_delegate_,
         ShowHistorySyncOptinUI(browser()->profile(), account_info_.account_id,
                                default_access_point));
+    ASSERT_NO_FATAL_FAILURE(ClickSyncButton());
+    histogram_tester.ExpectUniqueSample(
+        "Profile.Menu.ClickedActionableItem",
+        ProfileMenuViewBase::ActionableItem::kHistorySyncButton,
+        /*expected_bucket_count=*/1);
   } else {
+    // `Signin.SyncOptIn.Offered` should be recorded if the sync opt-in is
+    // offered from the profile menu. `Signin.HistorySyncOptIn.Offered` should
+    // not be recorded.
+    histogram_tester.ExpectUniqueSample("Signin.SyncOptIn.Offered",
+                                        default_access_point,
+                                        /*expected_bucket_count=*/1);
+    histogram_tester.ExpectTotalCount("Signin.HistorySyncOptIn.Offered",
+                                      /*expected_count=*/0);
+
     EXPECT_CALL(
         mock_signin_ui_delegate_,
         ShowTurnSyncOnUI(browser()->profile(), default_access_point,
@@ -2766,13 +2781,12 @@
                          TurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT,
                          /*is_sync_promo=*/false,
                          /*user_already_signed_in=*/true));
+    ASSERT_NO_FATAL_FAILURE(ClickSyncButton());
+    histogram_tester.ExpectUniqueSample(
+        "Profile.Menu.ClickedActionableItem",
+        ProfileMenuViewBase::ActionableItem::kSigninAccountButton,
+        /*expected_bucket_count=*/1);
   }
-  ASSERT_NO_FATAL_FAILURE(ClickSyncButton());
-  const ProfileMenuViewBase::ActionableItem actionable_item =
-      ProfileMenuViewBase::ActionableItem::kSigninAccountButton;
-  histogram_tester.ExpectUniqueSample("Profile.Menu.ClickedActionableItem",
-                                      actionable_item,
-                                      /*expected_bucket_count=*/1);
 }
 
 IN_PROC_BROWSER_TEST_F(ProfileMenuSigninAccessPointTest,
@@ -2790,9 +2804,11 @@
 
   if (base::FeatureList::IsEnabled(
           syncer::kReplaceSyncPromosWithSignInPromos)) {
-    histogram_tester.ExpectUniqueSample("Signin.SyncOptIn.Offered",
-                                        explicit_access_point,
-                                        /*expected_bucket_count=*/0);
+    // `Signin.SyncOptIn.Offered` should be not recorded if
+    // `syncer::kReplaceSyncPromosWithSignInPromos` is enabled. Instead,
+    // `Signin.HistorySyncOptIn.Offered` should be.
+    histogram_tester.ExpectTotalCount("Signin.SyncOptIn.Offered",
+                                      /*expected_count=*/0);
     histogram_tester.ExpectUniqueSample("Signin.HistorySyncOptIn.Offered",
                                         explicit_access_point,
                                         /*expected_bucket_count=*/1);
@@ -2803,14 +2819,17 @@
     ASSERT_NO_FATAL_FAILURE(ClickSyncButton());
     histogram_tester.ExpectUniqueSample(
         "Profile.Menu.ClickedActionableItem",
-        ProfileMenuViewBase::ActionableItem::kSigninAccountButton,
+        ProfileMenuViewBase::ActionableItem::kHistorySyncButton,
         /*expected_bucket_count=*/1);
   } else {
     // `Signin.SyncOptIn.Offered` should be recorded if the sync opt-in is
-    // offered from the profile menu.
+    // offered from the profile menu. `Signin.HistorySyncOptIn.Offered` should
+    // not be recorded.
     histogram_tester.ExpectUniqueSample("Signin.SyncOptIn.Offered",
                                         explicit_access_point,
                                         /*expected_bucket_count=*/1);
+    histogram_tester.ExpectTotalCount("Signin.HistorySyncOptIn.Offered",
+                                      /*expected_count=*/0);
 
     EXPECT_CALL(
         mock_signin_ui_delegate_,
diff --git a/chrome/browser/ui/views/session_restore_infobar/session_restore_infobar_interactive_uitest.cc b/chrome/browser/ui/views/session_restore_infobar/session_restore_infobar_interactive_uitest.cc
index ccbf6250..94a9abd1 100644
--- a/chrome/browser/ui/views/session_restore_infobar/session_restore_infobar_interactive_uitest.cc
+++ b/chrome/browser/ui/views/session_restore_infobar/session_restore_infobar_interactive_uitest.cc
@@ -76,6 +76,78 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
+class SessionRestoreInfobarDefaultTest : public InteractiveBrowserTest {
+ public:
+  SessionRestoreInfobarDefaultTest() {
+    feature_list_.InitWithFeaturesAndParameters(
+        {{features::kSessionRestoreInfobar,
+          {{features::kSetDefaultToContinueSession.name, "false"}}}},
+        {});
+  }
+  ~SessionRestoreInfobarDefaultTest() override = default;
+  SessionRestoreInfobarDefaultTest(const SessionRestoreInfobarDefaultTest&) =
+      delete;
+  SessionRestoreInfobarDefaultTest& operator=(
+      const SessionRestoreInfobarDefaultTest&) = delete;
+
+ protected:
+  void CreateInfobar(Browser* browser,
+                     bool was_restarted,
+                     bool is_post_crash_launch) {
+    auto* controller =
+        session_restore_infobar::SessionRestoreInfobarController::From(browser);
+    controller->MaybeShowInfoBar(*browser->profile(), was_restarted,
+                                 is_post_crash_launch);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+class SessionRestoreInfobarDefaultOffTest : public InteractiveBrowserTest {
+ public:
+  SessionRestoreInfobarDefaultOffTest() {
+    feature_list_.InitWithFeaturesAndParameters(
+        {{features::kSessionRestoreInfobar,
+          {{features::kSetDefaultToContinueSession.name, "true"}}}},
+        {});
+  }
+  ~SessionRestoreInfobarDefaultOffTest() override = default;
+  SessionRestoreInfobarDefaultOffTest(
+      const SessionRestoreInfobarDefaultOffTest&) = delete;
+  SessionRestoreInfobarDefaultOffTest& operator=(
+      const SessionRestoreInfobarDefaultOffTest&) = delete;
+
+ protected:
+  void CreateInfobar(Browser* browser,
+                     bool was_restarted,
+                     bool is_post_crash_launch) {
+    auto* controller =
+        session_restore_infobar::SessionRestoreInfobarController::From(browser);
+    controller->MaybeShowInfoBar(*browser->profile(), was_restarted,
+                                 is_post_crash_launch);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Test that the session restore infobar has the correct message value when the
+// browser session is restored.
+IN_PROC_BROWSER_TEST_F(SessionRestoreInfobarDefaultOffTest,
+                       InfobarMessageValueForRestart) {
+  CreateInfobar(browser(), true, false);
+
+  RunTestSequence(
+      WaitForShow(ConfirmInfoBar::kInfoBarElementId),
+      CheckView(ConfirmInfoBar::kInfoBarElementId, [](ConfirmInfoBar* infobar) {
+        return static_cast<SessionRestoreInfoBarDelegate*>(infobar->delegate())
+                   ->GetMessageText() ==
+               l10n_util::GetStringUTF16(
+                   IDS_SESSION_RESTORE_TURN_OFF_RESTORE_FROM_RESTART);
+      }));
+}
+
 // Test that the session restore infobar has no value set by the user and the
 // untouched session restore preference shows the correct message.
 IN_PROC_BROWSER_TEST_P(SessionRestoreInfobarInteractiveTest,
@@ -86,12 +158,8 @@
 
 // Test that the session restore infobar is shown on browser restart when the
 // session restore preference is at its default value.
-IN_PROC_BROWSER_TEST_P(SessionRestoreInfobarInteractiveTest,
+IN_PROC_BROWSER_TEST_F(SessionRestoreInfobarDefaultTest,
                        InfoBarShownOnRestartWithDefaultPref) {
-  if (IsDefaultContinueSession()) {
-    return;
-  }
-
   CreateInfobar(browser(), true, false);
   RunTestSequence(
       WaitForShow(ConfirmInfoBar::kInfoBarElementId),
@@ -134,34 +202,12 @@
   RunTestSequence(EnsureNotPresent(ConfirmInfoBar::kInfoBarElementId));
 }
 
-// Test that the session restore infobar has the correct message value when the
-// browser session is restored.
-IN_PROC_BROWSER_TEST_P(SessionRestoreInfobarInteractiveTest,
-                       InfobarMessageValueForRestart) {
-  if (!IsDefaultContinueSession()) {
-    return;
-  }
-  CreateInfobar(browser(), true, false);
-
-  RunTestSequence(
-      WaitForShow(ConfirmInfoBar::kInfoBarElementId),
-      CheckView(ConfirmInfoBar::kInfoBarElementId, [](ConfirmInfoBar* infobar) {
-        return static_cast<SessionRestoreInfoBarDelegate*>(infobar->delegate())
-                   ->GetMessageText() ==
-               l10n_util::GetStringUTF16(
-                   IDS_SESSION_RESTORE_TURN_OFF_RESTORE_FROM_RESTART);
-      }));
-}
 
 // Test that the session restore infobar has the correct message value when the
 // browser session is restored and the session restore preference is at its
 // default value.
-IN_PROC_BROWSER_TEST_P(SessionRestoreInfobarInteractiveTest,
+IN_PROC_BROWSER_TEST_F(SessionRestoreInfobarDefaultTest,
                        InfobarMessageValueForRestartWithDefaultPref) {
-  if (IsDefaultContinueSession()) {
-    return;
-  }
-
   CreateInfobar(browser(), true, false);
 
   RunTestSequence(
diff --git a/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc b/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
index b49c4b4..d0cffeb3 100644
--- a/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
@@ -53,6 +53,7 @@
 #include "components/user_education/common/feature_promo/feature_promo_controller.h"
 #include "components/user_education/common/feature_promo/feature_promo_result.h"
 #include "ui/actions/action_id.h"
+#include "ui/actions/actions.h"
 #include "ui/base/interaction/element_tracker.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_header_macros.h"
@@ -599,14 +600,13 @@
 }
 
 void SidePanelCoordinator::UpdateNewTabButtonState() {
-  views::ImageButton* const header_open_in_new_tab_button =
-      side_panel_header_->header_more_info_button();
-  if (header_open_in_new_tab_button && current_key()) {
+  if (current_key()) {
     SidePanelEntry* current_entry = GetEntryForUniqueKey(*current_key());
     bool has_open_in_new_tab_button =
         current_entry->SupportsNewTabButton() &&
         current_entry->GetOpenInNewTabURL().is_valid();
-    header_open_in_new_tab_button->SetVisible(has_open_in_new_tab_button);
+    side_panel_header_->header_open_in_new_tab_button()->SetVisible(
+        has_open_in_new_tab_button);
   }
 }
 
diff --git a/chrome/browser/ui/views/side_panel/side_panel_header.h b/chrome/browser/ui/views/side_panel/side_panel_header.h
index 822865ff..1f9004b 100644
--- a/chrome/browser/ui/views/side_panel/side_panel_header.h
+++ b/chrome/browser/ui/views/side_panel/side_panel_header.h
@@ -39,6 +39,10 @@
 
   views::ToggleImageButton* header_pin_button() { return header_pin_button_; }
 
+  views::ImageButton* header_open_in_new_tab_button() {
+    return header_open_in_new_tab_button_;
+  }
+
   views::ImageButton* header_more_info_button() {
     return header_more_info_button_;
   }
diff --git a/chrome/browser/ui/views/site_data/page_specific_site_data_dialog_interactive_uitest.cc b/chrome/browser/ui/views/site_data/page_specific_site_data_dialog_interactive_uitest.cc
index 70ede3b..b3d55f7 100644
--- a/chrome/browser/ui/views/site_data/page_specific_site_data_dialog_interactive_uitest.cc
+++ b/chrome/browser/ui/views/site_data/page_specific_site_data_dialog_interactive_uitest.cc
@@ -35,6 +35,7 @@
 #include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h"
 #include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/common/chrome_features.h"
@@ -410,8 +411,16 @@
                 .SetStartCallback(base::BindOnce(
                     [](Profile* profile, webapps::AppId app_id,
                        ui::InteractionSequence* seq, ui::TrackedElement* el) {
-                      web_app::LaunchBrowserForWebAppInTab(
-                          profile, app_id, WindowOpenDisposition::CURRENT_TAB);
+                      web_app::WebAppProvider* provider =
+                          web_app::WebAppProvider::GetForLocalAppsUnchecked(
+                              profile);
+                      provider->scheduler().LaunchAppWithCustomParams(
+                          apps::AppLaunchParams(
+                              app_id,
+                              apps::LaunchContainer::kLaunchContainerTab,
+                              WindowOpenDisposition::CURRENT_TAB,
+                              apps::LaunchSource::kFromTest),
+                          base::DoNothing());
                     },
                     browser()->profile(), app_id))),
         WaitForWebContentsNavigation(section_id, target_app_url));
diff --git a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc
index 907fba2..d56f7b5 100644
--- a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc
@@ -1847,8 +1847,11 @@
 
 // Invoked from the nested run loop.
 void DragToSeparateWindowStep2(DetachToBrowserTabDragControllerTest* test,
-                               TabStrip* not_attached_tab_strip,
-                               TabStrip* target_tab_strip) {
+                               BrowserWindowInterface* not_attached_browser,
+                               BrowserWindowInterface* target_browser) {
+  TabStrip* const not_attached_tab_strip =
+      GetTabStripForBrowser(not_attached_browser);
+  TabStrip* const target_tab_strip = GetTabStripForBrowser(target_browser);
   EXPECT_FALSE(not_attached_tab_strip->GetDragContext()->IsDragSessionActive());
   EXPECT_FALSE(target_tab_strip->GetDragContext()->IsDragSessionActive());
   EXPECT_TRUE(TabDragController::IsActive());
@@ -1858,10 +1861,10 @@
   // cleared on the source tabstrip.
   EXPECT_TRUE(test->IsTabDraggingInfoCleared(not_attached_tab_strip));
   // At this moment there should be a new browser window for the dragged tabs.
-  size_t num_browsers = test->browser_list()->size();
-  EXPECT_EQ(3u, num_browsers);
-  Browser* new_browser = test->browser_list()->get(num_browsers - 1);
-  TabStrip* new_tab_strip = GetTabStripForBrowser(new_browser);
+  EXPECT_EQ(3u, test->browser_list()->size());
+  BrowserWindowInterface* const new_browser =
+      ui_test_utils::GetBrowserNotInSet({not_attached_browser, target_browser});
+  TabStrip* const new_tab_strip = GetTabStripForBrowser(new_browser);
   EXPECT_TRUE(new_tab_strip->GetDragContext()->IsDragSessionActive());
   // Test that the tab dragging info should be correctly set on the new window.
   EXPECT_TRUE(test->IsTabDraggingInfoSet(new_tab_strip));
@@ -1917,7 +1920,7 @@
   // enough that it attaches to browser2.
   ASSERT_TRUE(DragInputToCenterNotifyWhenDone(
       tab,
-      base::BindOnce(&DragToSeparateWindowStep2, this, tab_strip, tab_strip2),
+      base::BindOnce(&DragToSeparateWindowStep2, this, browser(), browser2),
       gfx::Vector2d(0, GetDetachY(tab_strip))));
 
   // Wait for the browser containing the dragged tabs to be removed when
@@ -1997,7 +2000,7 @@
   // Drag from `tab_strip` to `tab_strip2`.
   ASSERT_TRUE(DragInputToCenterNotifyWhenDone(
       tab,
-      base::BindOnce(&DragToSeparateWindowStep2, this, tab_strip, tab_strip2),
+      base::BindOnce(&DragToSeparateWindowStep2, this, browser(), browser2),
       gfx::Vector2d(0, GetDetachY(tab_strip))));
 
   test::BrowserChangeWaiter(test::BrowserChangeWaiter::ChangeType::kRemoved)
@@ -2472,16 +2475,15 @@
   model->SelectTabAt(4);
 
   // Moves tab 2 to the front of the detached window.
-  delegate_->set_drop_callback(base::BindRepeating(
-      [](const BrowserList* browser_list,
-         TabDragDelegate::DragController& controller) {
+  delegate_->set_drop_callback(base::BindLambdaForTesting(
+      [&](TabDragDelegate::DragController& controller) {
         auto tab = controller.DetachTabAtForInsertion(1);
-        CHECK_EQ(2u, browser_list->size());
-        Browser* new_browser = browser_list->get(1);
-        new_browser->tab_strip_model()->InsertDetachedTabAt(0, std::move(tab),
-                                                            0, std::nullopt);
-      },
-      browser_list()));
+        CHECK_EQ(2u, browser_list()->size());
+        BrowserWindowInterface* const new_browser =
+            ui_test_utils::GetBrowserNotInSet({browser()});
+        new_browser->GetTabStripModel()->InsertDetachedTabAt(0, std::move(tab),
+                                                             0, std::nullopt);
+      }));
 
   Tab* tab = tab_strip->tab_at(0);
   const int tab_width = tab->width();
@@ -2535,8 +2537,9 @@
                        DetachToOwnWindowWithNonVisibleOnAllWorkspaceState) {
   // Set the source browser to be visible on all workspace.
   ASSERT_EQ(1u, browser_list()->size());
-  Browser* source_browser = browser_list()->get(0);
-  auto* source_window = source_browser->window()->GetNativeWindow();
+  BrowserWindowInterface* const source_browser =
+      GetLastActiveBrowserWindowInterfaceWithAnyProfile();
+  auto* source_window = source_browser->GetWindow()->GetNativeWindow();
   source_window->SetProperty(
       aura::client::kWindowWorkspaceKey,
       aura::client::kWindowWorkspaceVisibleOnAllWorkspaces);
@@ -3590,11 +3593,11 @@
 void PressEscapeWhileDetachedHeaderStep2(
     DetachToBrowserTabDragControllerTest* test) {
   // At this moment there should be a new browser window for the dragged tabs.
-  size_t num_browsers = test->browser_list()->size();
-  EXPECT_EQ(2u, num_browsers);
-  Browser* new_browser = test->browser_list()->get(num_browsers - 1);
+  EXPECT_EQ(2u, test->browser_list()->size());
+  BrowserWindowInterface* const new_browser =
+      ui_test_utils::GetBrowserNotInSet({test->browser()});
   std::vector<tab_groups::TabGroupId> new_browser_groups =
-      new_browser->tab_strip_model()->group_model()->ListTabGroups();
+      new_browser->GetTabStripModel()->group_model()->ListTabGroups();
   EXPECT_EQ(1u, new_browser_groups.size());
   EXPECT_EQ(0u, test->browser()
                     ->tab_strip_model()
@@ -3607,8 +3610,8 @@
   EXPECT_TRUE(new_group_header->dragging());
 
   EXPECT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
-      new_browser->window()->GetNativeWindow(), ui::VKEY_ESCAPE, false, false,
-      false, false));
+      new_browser->GetWindow()->GetNativeWindow(), ui::VKEY_ESCAPE, false,
+      false, false, false));
 }
 
 }  // namespace
@@ -3827,7 +3830,7 @@
   ASSERT_TRUE(PressInputAtCenter(group_header));
   ASSERT_TRUE(DragInputToCenterNotifyWhenDone(
       group_header,
-      base::BindOnce(&DragToSeparateWindowStep2, this, tab_strip, tab_strip2),
+      base::BindOnce(&DragToSeparateWindowStep2, this, browser(), browser2),
       gfx::Vector2d(0, GetDetachY(tab_strip))));
   test::BrowserChangeWaiter(test::BrowserChangeWaiter::ChangeType::kRemoved)
       .Wait(base::BindLambdaForTesting(
@@ -4543,13 +4546,13 @@
                                 Browser* browser,
                                 TabStrip* tab_strip) {
   // There should be another browser.
-  size_t num_browsers = test->browser_list()->size();
-  EXPECT_EQ(2u, num_browsers);
-  Browser* new_browser = test->browser_list()->get(num_browsers - 1);
+  EXPECT_EQ(2u, test->browser_list()->size());
+  BrowserWindowInterface* const new_browser =
+      ui_test_utils::GetBrowserNotInSet({browser});
   EXPECT_NE(browser, new_browser);
   ui_test_utils::BrowserActivationWaiter activation_waiter(new_browser);
   activation_waiter.WaitForActivation();
-  EXPECT_TRUE(new_browser->window()->IsActive());
+  EXPECT_TRUE(new_browser->GetWindow()->IsActive());
   TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
 
   EXPECT_TRUE(tab_strip2->GetDragContext()->IsDragSessionActive());
@@ -4603,27 +4606,6 @@
   EXPECT_TRUE(new_browser->GetWindow()->IsMaximized());
 }
 
-namespace {
-
-void NewBrowserWindowStateStep2(DetachToBrowserTabDragControllerTest* test,
-                                TabStrip* tab_strip) {
-  // There should be two browser windows, including the newly created one for
-  // the dragged tab.
-  EXPECT_EQ(3u, test->browser_list()->size());
-
-  // Get this new created window for the dragged tab.
-  Browser* new_browser = test->browser_list()->get(2);
-  aura::Window* window = new_browser->window()->GetNativeWindow();
-  EXPECT_NE(window->GetProperty(aura::client::kShowStateKey),
-            ui::mojom::WindowShowState::kMaximized);
-  EXPECT_EQ(window->GetProperty(aura::client::kShowStateKey),
-            ui::mojom::WindowShowState::kDefault);
-
-  EXPECT_TRUE(test->ReleaseInput());
-}
-
-}  // namespace
-
 // Test that tab dragging can work on a browser window with its initial show
 // state is MAXIMIZED.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
@@ -4633,7 +4615,6 @@
   params.initial_show_state = ui::mojom::WindowShowState::kMaximized;
   Browser* browser = Browser::Create(params);
   AddBlankTabAndShow(browser);
-  TabStrip* tab_strip = GetTabStripForBrowser(browser);
   AddTabsAndResetBrowser(browser, 1);
 
   // Maximize the browser window.
@@ -4643,8 +4624,23 @@
             ui::mojom::WindowShowState::kMaximized);
 
   // Drag it far enough that the first tab detaches.
-  DragTabAndNotify(
-      tab_strip, base::BindOnce(&NewBrowserWindowStateStep2, this, tab_strip));
+  DragTabForDetachAndNotify(
+      browser,
+      base::BindLambdaForTesting([&](BrowserWindowInterface* source_browser,
+                                     BrowserWindowInterface* detached_browser) {
+        // There should be two browser windows, including the newly created one
+        // for the dragged tab.
+        EXPECT_EQ(3u, browser_list()->size());
+
+        aura::Window* const window =
+            detached_browser->GetWindow()->GetNativeWindow();
+        EXPECT_NE(window->GetProperty(aura::client::kShowStateKey),
+                  ui::mojom::WindowShowState::kMaximized);
+        EXPECT_EQ(window->GetProperty(aura::client::kShowStateKey),
+                  ui::mojom::WindowShowState::kDefault);
+
+        EXPECT_TRUE(ReleaseInput());
+      }));
 }
 
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
@@ -4776,7 +4772,7 @@
 
 // Returns true if the web contents that's associated with `browser` is using
 // fast resize.
-bool WebContentsIsFastResized(Browser* browser) {
+bool WebContentsIsFastResized(BrowserWindowInterface* browser) {
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
   ContentsWebView* contents_web_view =
       static_cast<ContentsWebView*>(browser_view->GetContentsView());
@@ -4784,16 +4780,17 @@
 }
 
 void FastResizeDuringDraggingStep2(DetachToBrowserTabDragControllerTest* test,
-                                   TabStrip* not_attached_tab_strip,
-                                   TabStrip* target_tab_strip) {
+                                   BrowserWindowInterface* not_attached_browser,
+                                   BrowserWindowInterface* target_browser) {
+  TabStrip* const target_tab_strip = GetTabStripForBrowser(target_browser);
   // There should be three browser windows, including the newly created one for
   // the dragged tab.
-  size_t num_browsers = test->browser_list()->size();
-  EXPECT_EQ(3u, num_browsers);
+  EXPECT_EQ(3u, test->browser_list()->size());
 
 #if !BUILDFLAG(IS_LINUX)
   // Get this new created window for the drag. It should have fast resize set.
-  Browser* new_browser = test->browser_list()->get(num_browsers - 1);
+  BrowserWindowInterface* const new_browser =
+      ui_test_utils::GetBrowserNotInSet({not_attached_browser, target_browser});
   EXPECT_TRUE(WebContentsIsFastResized(new_browser));
   // The source window should also have fast resize set.
   EXPECT_TRUE(WebContentsIsFastResized(test->browser()));
@@ -4812,12 +4809,11 @@
 // resize after tab dragging ends.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        FastResizeDuringDragging) {
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+  TabStrip* const tab_strip = GetTabStripForBrowser(browser());
   AddTabsAndResetBrowser(browser(), 1);
 
   // Create another browser.
-  Browser* browser2 = CreateAnotherBrowserAndResize();
-  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
+  BrowserWindowInterface* const browser2 = CreateAnotherBrowserAndResize();
   EXPECT_EQ(2u, browser_list()->size());
 
   EXPECT_FALSE(WebContentsIsFastResized(browser()));
@@ -4826,7 +4822,7 @@
   // Move to the first tab and drag it enough so that it detaches, but not
   // enough that it attaches to browser2.
   DragTabAndNotify(tab_strip, base::BindOnce(&FastResizeDuringDraggingStep2,
-                                             this, tab_strip, tab_strip2));
+                                             this, browser(), browser2));
 
   EXPECT_FALSE(WebContentsIsFastResized(browser()));
   EXPECT_FALSE(WebContentsIsFastResized(browser2));
@@ -4862,7 +4858,8 @@
                        DragAppToOwnWindow) {
   // Install and get a tabbed system app.
   webapps::AppId tabbed_app_id = InstallMockApp();
-  Browser* app_browser = LaunchWebAppBrowser(tabbed_app_id);
+  BrowserWindowInterface* const app_browser =
+      LaunchWebAppBrowser(tabbed_app_id);
   ASSERT_EQ(2u, browser_list()->size());
 
   // Close normal browser since other code expects only 1 browser to start.
@@ -4870,7 +4867,7 @@
   ASSERT_EQ(1u, browser_list()->size());
   SelectFirstBrowser();
   ASSERT_EQ(app_browser, browser());
-  EXPECT_EQ(Browser::Type::TYPE_APP, browser_list()->get(0)->type());
+  EXPECT_EQ(BrowserWindowInterface::Type::TYPE_APP, browser()->GetType());
   AddTabsAndResetBrowser(browser(), 1, GetAppUrl());
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
@@ -4914,13 +4911,12 @@
   ASSERT_EQ(app_browser1, browser());
 
   AddTabsAndResetBrowser(browser(), 1, GetAppUrl());
-  TabStrip* tab_strip1 = GetTabStripForBrowser(app_browser1);
-  TabStrip* tab_strip2 = GetTabStripForBrowser(app_browser2);
+  TabStrip* const tab_strip1 = GetTabStripForBrowser(app_browser1);
 
   // Move to the first tab and drag it enough so that it detaches, but not
   // enough that it attaches to browser2.
   DragTabAndNotify(tab_strip1, base::BindOnce(&DragToSeparateWindowStep2, this,
-                                              tab_strip1, tab_strip2));
+                                              app_browser1, app_browser2));
 
   // Should now be attached to tab_strip2.
   // Release mouse or touch, stopping the drag session.
@@ -5521,19 +5517,21 @@
 // Invoked from the nested run loop.
 void CancelDragTabToWindowInSeparateDisplayStep2(
     DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest* test,
-    TabStrip* tab_strip,
+    BrowserWindowInterface* browser,
     Display current_display,
     gfx::Point final_destination) {
+  TabStrip* const tab_strip = GetTabStripForBrowser(browser);
+
   EXPECT_FALSE(tab_strip->GetDragContext()->IsDragSessionActive());
   EXPECT_TRUE(TabDragController::IsActive());
-  size_t num_browsers = test->browser_list()->size();
-  EXPECT_EQ(2u, num_browsers);
+  EXPECT_EQ(2u, test->browser_list()->size());
 
-  Browser* new_browser = test->browser_list()->get(num_browsers - 1);
+  BrowserWindowInterface* const new_browser =
+      ui_test_utils::GetBrowserNotInSet({browser});
   EXPECT_EQ(
       current_display.id(),
       display::Screen::Get()
-          ->GetDisplayNearestWindow(new_browser->window()->GetNativeWindow())
+          ->GetDisplayNearestWindow(new_browser->GetWindow()->GetNativeWindow())
           .id());
 
   EXPECT_TRUE(test->DragInputToNotifyWhenDone(
@@ -5574,7 +5572,7 @@
   DragTabAndNotify(
       tab_strip,
       base::BindOnce(&CancelDragTabToWindowInSeparateDisplayStep2, this,
-                     tab_strip, displays.first, final_destination));
+                     browser(), displays.first, final_destination));
 
   ASSERT_EQ(1u, browser_list()->size());
   ASSERT_FALSE(tab_strip->GetDragContext()->IsDragSessionActive());
@@ -5625,7 +5623,7 @@
   DragTabAndNotify(
       tab_strip,
       base::BindOnce(&CancelDragTabToWindowInSeparateDisplayStep2, this,
-                     tab_strip, displays.second, final_destination));
+                     browser(), displays.second, final_destination));
 
   ASSERT_EQ(1u, browser_list()->size());
   ASSERT_FALSE(tab_strip->GetDragContext()->IsDragSessionActive());
@@ -5658,37 +5656,6 @@
   std::unique_ptr<base::SimpleTestTickClock> clock_;
 };
 
-namespace {
-void PressSecondFingerWhileDetachedStep3(
-    DetachToBrowserTabDragControllerTest* test) {
-  EXPECT_TRUE(TabDragController::IsActive());
-  EXPECT_EQ(2u, test->browser_list()->size());
-  EXPECT_TRUE(test->browser_list()->get(1)->window()->IsActive());
-
-  EXPECT_TRUE(test->ReleaseInput());
-  EXPECT_TRUE(test->ReleaseInput(1));
-}
-
-void PressSecondFingerWhileDetachedStep2(
-    DetachToBrowserTabDragControllerTest* test,
-    const gfx::Point& target_point) {
-  EXPECT_TRUE(TabDragController::IsActive());
-  size_t num_browsers = test->browser_list()->size();
-  EXPECT_EQ(2u, num_browsers);
-  EXPECT_TRUE(
-      test->browser_list()->get(num_browsers - 1)->window()->IsActive());
-
-  // The window hint isn't used on Ash.
-  gfx::NativeWindow window_hint = gfx::NativeWindow();
-  // Continue dragging after adding a second finger.
-  EXPECT_TRUE(test->PressInput(gfx::Point(), window_hint, 1));
-  EXPECT_TRUE(test->DragInputToNotifyWhenDone(
-      target_point, base::BindOnce(&PressSecondFingerWhileDetachedStep3, test),
-      window_hint));
-}
-
-}  // namespace
-
 // Detaches a tab and while detached presses a second finger.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
                        PressSecondFingerWhileDetached) {
@@ -5702,14 +5669,32 @@
   const int touch_move_delta = GetDetachY(tab_strip);
   const gfx::Point target = GetCenterInScreenCoordinates(tab_strip->tab_at(0)) +
                             gfx::Vector2d(0, 2 * touch_move_delta);
-  ui_test_utils::BrowserCreatedObserver browser_created_observer;
-  DragTabAndNotify(
-      tab_strip,
-      base::BindOnce(&PressSecondFingerWhileDetachedStep2, this, target), 0,
-      touch_move_delta + 5);
 
-  // There should now be another browser.
-  BrowserWindowInterface* const new_browser = browser_created_observer.Wait();
+  BrowserWindowInterface* const new_browser = DragTabForDetachAndNotify(
+      browser(),
+      base::BindLambdaForTesting([&](BrowserWindowInterface* source_browser,
+                                     BrowserWindowInterface* detached_browser) {
+        EXPECT_TRUE(TabDragController::IsActive());
+        EXPECT_TRUE(detached_browser->GetWindow()->IsActive());
+
+        // The window hint isn't used on Ash.
+        gfx::NativeWindow window_hint = gfx::NativeWindow();
+        // Continue dragging after adding a second finger.
+        EXPECT_TRUE(PressInput(gfx::Point(), window_hint, 1));
+        EXPECT_TRUE(DragInputToNotifyWhenDone(
+            target, base::BindLambdaForTesting([&]() {
+              EXPECT_TRUE(TabDragController::IsActive());
+              EXPECT_EQ(2u, browser_list()->size());
+              EXPECT_TRUE(ui_test_utils::GetBrowserNotInSet({browser()})
+                              ->GetWindow()
+                              ->IsActive());
+
+              EXPECT_TRUE(ReleaseInput());
+              EXPECT_TRUE(ReleaseInput(1));
+            }),
+            window_hint));
+      }),
+      0, touch_move_delta + 5);
   ASSERT_TRUE(new_browser);
 
   // Should no longer be dragging.
@@ -5810,6 +5795,7 @@
 
   // Sends events to the server without waiting for its reply, which will cause
   // extra touch events before PerformWindowMove starts handling events.
+  ui_test_utils::BrowserCreatedObserver browser_created_observer;
   test::QuitDraggingObserver observer(tab_strip);
   clock_ = std::make_unique<base::SimpleTestTickClock>();
   clock_->SetNowTicks(base::TimeTicks::Now());
@@ -5822,13 +5808,13 @@
   clock_->Advance(base::Milliseconds(2));
   ASSERT_TRUE(ReleaseInput());
   observer.Wait();
+  BrowserWindowInterface* const browser2 = browser_created_observer.Wait();
 
   ASSERT_FALSE(tab_strip->GetDragContext()->IsDragSessionActive());
   ASSERT_FALSE(TabDragController::IsActive());
   EXPECT_EQ(2u, browser_list()->size());
-  auto* browser2 = browser_list()->get(1);
-  EXPECT_TRUE(browser2->window()->IsMinimized());
-  EXPECT_FALSE(browser2->window()->IsVisible());
+  EXPECT_TRUE(browser2->GetWindow()->IsMinimized());
+  EXPECT_FALSE(browser2->GetWindow()->IsVisible());
 }
 
 #endif  // BUILDFLAG(IS_CHROMEOS)
@@ -5887,7 +5873,8 @@
                        DoesNotMoveTabOnDrag) {
   // Install and launch mock app that can be locked for OnTask.
   const webapps::AppId tabbed_app_id = InstallMockApp();
-  Browser* const app_browser = LaunchWebAppBrowser(tabbed_app_id);
+  BrowserWindowInterface* const app_browser =
+      LaunchWebAppBrowser(tabbed_app_id);
   ASSERT_EQ(2u, browser_list()->size());
 
   // Close normal browser.
@@ -5895,7 +5882,7 @@
   ASSERT_EQ(1u, browser_list()->size());
   SelectFirstBrowser();
   ASSERT_EQ(app_browser, browser());
-  EXPECT_EQ(Browser::Type::TYPE_APP, browser_list()->get(0)->type());
+  EXPECT_EQ(Browser::Type::TYPE_APP, browser()->GetType());
 
   // Lock the app for OnTask and set up app for testing drag behavior.
   browser()->SetLockedForOnTask(true);
@@ -5920,7 +5907,8 @@
                        TabDoesNotDetachOnDrag) {
   // Install and launch mock app that can be locked for OnTask.
   const webapps::AppId tabbed_app_id = InstallMockApp();
-  Browser* const app_browser = LaunchWebAppBrowser(tabbed_app_id);
+  BrowserWindowInterface* const app_browser =
+      LaunchWebAppBrowser(tabbed_app_id);
   ASSERT_EQ(2u, browser_list()->size());
 
   // Close normal browser.
@@ -5928,7 +5916,7 @@
   ASSERT_EQ(1u, browser_list()->size());
   SelectFirstBrowser();
   ASSERT_EQ(app_browser, browser());
-  EXPECT_EQ(Browser::Type::TYPE_APP, browser_list()->get(0)->type());
+  EXPECT_EQ(Browser::Type::TYPE_APP, browser()->GetType());
 
   // Lock the app for OnTask and set up app for testing drag behavior.
   browser()->SetLockedForOnTask(true);
@@ -5948,7 +5936,8 @@
                        WindowDoesNotMoveOnTabDrag) {
   // Install and launch mock app that can be locked for OnTask.
   const webapps::AppId tabbed_app_id = InstallMockApp();
-  Browser* const app_browser = LaunchWebAppBrowser(tabbed_app_id);
+  BrowserWindowInterface* const app_browser =
+      LaunchWebAppBrowser(tabbed_app_id);
   ASSERT_EQ(2u, browser_list()->size());
 
   // Close normal browser.
@@ -5956,7 +5945,7 @@
   ASSERT_EQ(1u, browser_list()->size());
   SelectFirstBrowser();
   ASSERT_EQ(app_browser, browser());
-  EXPECT_EQ(Browser::Type::TYPE_APP, browser_list()->get(0)->type());
+  EXPECT_EQ(Browser::Type::TYPE_APP, browser()->GetType());
 
   // Lock the app for OnTask.
   browser()->SetLockedForOnTask(true);
diff --git a/chrome/browser/ui/views/tabs/glic_button.cc b/chrome/browser/ui/views/tabs/glic_button.cc
index 1a7b166..c48353e 100644
--- a/chrome/browser/ui/views/tabs/glic_button.cc
+++ b/chrome/browser/ui/views/tabs/glic_button.cc
@@ -130,6 +130,7 @@
                        PressedCallback close_pressed_callback,
                        base::RepeatingClosure hovered_callback,
                        base::RepeatingClosure mouse_down_callback,
+                       base::RepeatingClosure expansion_animation_done_callback,
                        const std::u16string& tooltip)
     : TabStripNudgeButton(tab_strip_controller,
                           std::move(pressed_callback),
@@ -143,6 +144,8 @@
       tab_strip_controller_(tab_strip_controller),
       hovered_callback_(std::move(hovered_callback)),
       mouse_down_callback_(std::move(mouse_down_callback)),
+      expansion_animation_done_callback_(
+          std::move(expansion_animation_done_callback)),
       normal_icon_(GetNormalIcon()),
       icon_for_highlight_(GetIconForHighlight()) {
   SetProperty(views::kElementIdentifierKey, kGlicButtonElementId);
@@ -367,6 +370,17 @@
   }
 }
 
+void GlicButton::AnimationEnded(const gfx::Animation* animation) {
+  if (animation == expansion_animation_.get()) {
+    AnimationProgressed(animation);
+    expansion_animation_done_callback_.Run();
+  }
+}
+
+void GlicButton::AnimationCanceled(const gfx::Animation* animation) {
+  AnimationEnded(animation);
+}
+
 bool GlicButton::IsContextMenuShowingForTest() {
   return menu_runner_ && menu_runner_->IsRunning();
 }
@@ -551,12 +565,16 @@
 }
 
 int GlicButton::CalculateExpandedWidth() {
-  // Measure the nudge text.
-  auto render_text = gfx::RenderText::CreateRenderText();
-  CHECK(pending_text_);
-  render_text->SetText(*pending_text_);
-  render_text->SetFontList(label()->font_list());
-  const int nudge_text_width = render_text->GetStringSize().width();
+  int nudge_text_width = 0;
+  // May be unset in tests.
+  // TODO(449773402): pending_text_ should always be set here.
+  if (pending_text_) {
+    // Measure the nudge text.
+    auto render_text = gfx::RenderText::CreateRenderText();
+    render_text->SetText(*pending_text_);
+    render_text->SetFontList(label()->font_list());
+    nudge_text_width = render_text->GetStringSize().width();
+  }
 
   const int old_width = GetLayoutManager()->GetPreferredSize(this).width();
   // Replace old label with new.
@@ -578,8 +596,10 @@
 
   // Button width animation updates width_factor_, used in
   // CalculatePreferredSize().
-  expansion_animation_ = std::make_unique<gfx::SlideAnimation>(this);
-  expansion_animation_->SetTweenType(kTween);
+  if (!expansion_animation_) {
+    expansion_animation_ = std::make_unique<gfx::SlideAnimation>(this);
+    expansion_animation_->SetTweenType(kTween);
+  }
   expansion_animation_->SetSlideDuration(overall_duration);
   if (show) {
     expansion_animation_->Show();
@@ -662,6 +682,10 @@
   PreferredSizeChanged();
 }
 
+gfx::SlideAnimation* GlicButton::GetExpansionAnimationForTesting() {
+  return expansion_animation_.get();
+}
+
 BEGIN_METADATA(GlicButton)
 END_METADATA
 
diff --git a/chrome/browser/ui/views/tabs/glic_button.h b/chrome/browser/ui/views/tabs/glic_button.h
index 5bfe7018..99ee85a 100644
--- a/chrome/browser/ui/views/tabs/glic_button.h
+++ b/chrome/browser/ui/views/tabs/glic_button.h
@@ -36,6 +36,7 @@
                       PressedCallback close_pressed_callback,
                       base::RepeatingClosure hovered_callback,
                       base::RepeatingClosure mouse_down_callback,
+                      base::RepeatingClosure expansion_animation_done_callback,
                       const std::u16string& tooltip);
   GlicButton(const GlicButton&) = delete;
   GlicButton& operator=(const GlicButton&) = delete;
@@ -75,6 +76,8 @@
 
   // gfx::AnimationDelegate:
   void AnimationProgressed(const gfx::Animation* animation) override;
+  void AnimationEnded(const gfx::Animation* animation) override;
+  void AnimationCanceled(const gfx::Animation* animation) override;
 
   bool IsContextMenuShowingForTest();
 
@@ -87,6 +90,8 @@
   // Called when the slide animation finishes.
   void OnAnimationEnded();
 
+  gfx::SlideAnimation* GetExpansionAnimationForTesting() override;
+
  private:
   // views::LabelButton:
   void SetText(std::u16string_view text) override;
@@ -163,6 +168,9 @@
   // (i.e., the user is very likely to interact with it soon).
   base::RepeatingClosure mouse_down_callback_;
 
+  // Invoked when the button hide animation finishes.
+  base::RepeatingClosure expansion_animation_done_callback_;
+
   // Cached widths for animating label changes.
   int initial_width_ = 0;
   int expanded_width_ = 0;
diff --git a/chrome/browser/ui/views/tabs/tab_strip_action_container.cc b/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
index d277cf5..f934a5e 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
@@ -90,14 +90,6 @@
 #endif  // !BUILDFLAG(IS_MAC)
 #endif  // BUILDFLAG(ENABLE_GLIC)
 
-bool ButtonOwnsAnimation() {
-#if BUILDFLAG(ENABLE_GLIC)
-  return base::FeatureList::IsEnabled(features::kGlicEntrypointVariations);
-#else
-  return false;
-#endif
-}
-
 }  // namespace
 
 TabStripActionContainer::TabStripNudgeAnimationSession::
@@ -431,6 +423,9 @@
                               base::Unretained(this)),
           base::BindRepeating(&TabStripActionContainer::OnGlicButtonMouseDown,
                               base::Unretained(this)),
+          base::BindRepeating(
+              &TabStripActionContainer::OnGlicButtonAnimationEnded,
+              base::Unretained(this)),
           tooltip_text);
 
   glic_button->SetProperty(views::kCrossAxisAlignmentKey,
@@ -617,6 +612,16 @@
   }
 }
 
+void TabStripActionContainer::OnGlicButtonAnimationEnded() {
+  if (!glic_button_->GetIsShowingNudge()) {
+    scoped_tab_strip_modal_ui_.reset();
+
+    if (locked_expansion_button_) {
+      locked_expansion_button_->SetIsShowingNudge(false);
+    }
+  }
+}
+
 void TabStripActionContainer::OnGlicActorTaskIconClicked() {
   Profile* profile = tab_strip_controller_->GetProfile();
   glic::GlicKeyedServiceFactory::GetGlicKeyedService(profile)->ToggleUI(
@@ -901,7 +906,7 @@
   scoped_tab_strip_modal_ui_.reset();
   scoped_tab_strip_modal_ui_ = tab_strip_controller_->ShowModalUI();
 
-  if (!ButtonOwnsAnimation()) {
+  if (!ButtonOwnsAnimation(button)) {
     animation_session_ = std::make_unique<TabStripNudgeAnimationSession>(
         button, this, TabStripNudgeAnimationSession::AnimationSessionType::SHOW,
         base::BindOnce(&TabStripActionContainer::OnAnimationSessionEnded,
@@ -934,7 +939,8 @@
   // Since the glic button is still visible in it's hidden state we need to have
   // a special case to query if it's in its Hide state.
 #if BUILDFLAG(ENABLE_GLIC)
-  if (button == glic_button_ && button->GetWidthFactor() == 0.0) {
+  if (button == glic_button_ && button->GetWidthFactor() == 0.0 &&
+      !ButtonOwnsAnimation(button)) {
     return;
   }
 #endif  // BUILDFLAG(ENABLE_GLIC)
@@ -943,7 +949,7 @@
   // Stop the timer since the chip might be getting hidden on user actions like
   // dismissal or click and not timeout.
   hide_tab_strip_nudge_timer_.Stop();
-  if (!ButtonOwnsAnimation()) {
+  if (!ButtonOwnsAnimation(button)) {
     animation_session_ = std::make_unique<TabStripNudgeAnimationSession>(
         button, this, TabStripNudgeAnimationSession::AnimationSessionType::HIDE,
         base::BindOnce(&TabStripActionContainer::OnAnimationSessionEnded,
@@ -1108,5 +1114,15 @@
   separator_->SetColorId(kColorTabDividerFrameInactive);
 }
 
+bool TabStripActionContainer::ButtonOwnsAnimation(
+    const TabStripNudgeButton* button) const {
+#if BUILDFLAG(ENABLE_GLIC)
+  return button == glic_button_ &&
+         base::FeatureList::IsEnabled(features::kGlicEntrypointVariations);
+#else
+  return false;
+#endif
+}
+
 BEGIN_METADATA(TabStripActionContainer)
 END_METADATA
diff --git a/chrome/browser/ui/views/tabs/tab_strip_action_container.h b/chrome/browser/ui/views/tabs/tab_strip_action_container.h
index 759618c5..9b4f5c7d 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_action_container.h
+++ b/chrome/browser/ui/views/tabs/tab_strip_action_container.h
@@ -179,6 +179,7 @@
   void OnGlicButtonDismissed();
   void OnGlicButtonHovered();
   void OnGlicButtonMouseDown();
+  void OnGlicButtonAnimationEnded();
 
   std::unique_ptr<glic::GlicActorTaskIcon> CreateGlicActorTaskIcon(
       TabStripController* tab_strip_controller);
@@ -225,6 +226,8 @@
 
   void OnAnimationSessionEnded();
 
+  bool ButtonOwnsAnimation(const TabStripNudgeButton* button) const;
+
   std::unique_ptr<TabStripNudgeButton> CreateAutoTabGroupButton(
       TabStripController* tab_strip_controller);
   std::unique_ptr<TabStripNudgeButton> CreateTabDeclutterButton(
diff --git a/chrome/browser/ui/views/tabs/tab_strip_action_container_browsertest.cc b/chrome/browser/ui/views/tabs/tab_strip_action_container_browsertest.cc
index 3ea767b..77d30d9d 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_action_container_browsertest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_action_container_browsertest.cc
@@ -164,6 +164,15 @@
     tab_strip_action_container()->OnTabStripNudgeButtonTimeout(button);
   }
 
+  gfx::SlideAnimation* GetExpansionAnimation(TabStripNudgeButton* button) {
+    if (tab_strip_action_container()->ButtonOwnsAnimation(button)) {
+      return button->GetExpansionAnimationForTesting();
+    }
+    return tab_strip_action_container()
+        ->animation_session_for_testing()
+        ->expansion_animation();
+  }
+
   void SetLockedExpansionMode(LockedExpansionMode mode,
                               TabStripNudgeButton* button) {
     tab_strip_action_container()->SetLockedExpansionMode(mode, button);
@@ -444,24 +453,19 @@
 IN_PROC_BROWSER_TEST_F(TabStripActionContainerBrowserTest,
                        ImmediatelyHidesWhenGlicNudgeButtonDismissed) {
   ShowTabStripNudgeButton(GlicNudgeButton());
-  ResetAnimation(1);
+  GetExpansionAnimation(GlicNudgeButton())->Reset(1);
   tab_strip_action_container()->GetWidget()->LayoutRootViewIfNecessary();
 
   SetLockedExpansionMode(LockedExpansionMode::kWillHide, GlicNudgeButton());
 
   OnButtonDismissed(GlicNudgeButton());
-
-  EXPECT_TRUE(tab_strip_action_container()
-                  ->animation_session_for_testing()
-                  ->expansion_animation()
-                  ->IsClosing());
+  EXPECT_TRUE(GetExpansionAnimation(GlicNudgeButton())->IsClosing());
 }
 
 IN_PROC_BROWSER_TEST_F(TabStripActionContainerBrowserTest,
                        LogsWhenGlicNudgeButtonClicked) {
   ShowTabStripNudgeButton(GlicNudgeButton());
 
-  ResetAnimation(1);
   tab_strip_action_container()->GetWidget()->LayoutRootViewIfNecessary();
 
   OnButtonClicked(GlicNudgeButton());
@@ -513,8 +517,7 @@
 IN_PROC_BROWSER_TEST_F(TabStripActionContainerBrowserTest,
                        ShowAndHideGlicButtonWhenGlicNudgeButtonShows) {
   ShowTabStripNudgeButton(GlicNudgeButton());
-
-  ResetAnimation(1);
+  GetExpansionAnimation(GlicNudgeButton())->Reset(1);
   tab_strip_action_container()->GetWidget()->LayoutRootViewIfNecessary();
 
   EXPECT_EQ(1, tab_strip_action_container()
@@ -524,7 +527,7 @@
 
   OnButtonDismissed(GlicNudgeButton());
 
-  ResetAnimation(0);
+  GetExpansionAnimation(GlicNudgeButton())->Reset(0);
   EXPECT_EQ(0, tab_strip_action_container()
                    ->GetGlicButton()
                    ->width_factor_for_testing());
diff --git a/chrome/browser/ui/views/tabs/tab_strip_nudge_button.cc b/chrome/browser/ui/views/tabs/tab_strip_nudge_button.cc
index 9981604..45dc947d 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_nudge_button.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_nudge_button.cc
@@ -179,5 +179,9 @@
   close_button_->SetFocusBehavior(focus_behavior);
 }
 
+gfx::SlideAnimation* TabStripNudgeButton::GetExpansionAnimationForTesting() {
+  return nullptr;
+}
+
 BEGIN_METADATA(TabStripNudgeButton)
 END_METADATA
diff --git a/chrome/browser/ui/views/tabs/tab_strip_nudge_button.h b/chrome/browser/ui/views/tabs/tab_strip_nudge_button.h
index d335e046..b5c2f3c 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_nudge_button.h
+++ b/chrome/browser/ui/views/tabs/tab_strip_nudge_button.h
@@ -11,6 +11,10 @@
 
 class TabStripController;
 
+namespace gfx {
+class SlideAnimation;
+}
+
 class TabStripNudgeButton : public TabStripControlButton {
   METADATA_HEADER(TabStripNudgeButton, TabStripControlButton)
 
@@ -43,6 +47,8 @@
 
   bool GetIsShowingNudge() { return is_showing_nudge_; }
 
+  virtual gfx::SlideAnimation* GetExpansionAnimationForTesting();
+
  protected:
   // TabStripControlButton:
   int GetCornerRadius() const override;
diff --git a/chrome/browser/ui/views/toolbar/pinned_action_toolbar_button.cc b/chrome/browser/ui/views/toolbar/pinned_action_toolbar_button.cc
index f867461..3c2697f 100644
--- a/chrome/browser/ui/views/toolbar/pinned_action_toolbar_button.cc
+++ b/chrome/browser/ui/views/toolbar/pinned_action_toolbar_button.cc
@@ -31,7 +31,6 @@
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/actions/action_id.h"
-#include "ui/actions/action_utils.h"
 #include "ui/actions/actions.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/models/menu_separator_types.h"
@@ -380,7 +379,8 @@
   // item, use the stateful image. Otherwise, use the action item's image.
   ui::ImageModel image_model;
 
-  if (IsActionItemClass<actions::StatefulImageActionItem>(action_item)) {
+  if (actions::IsActionItemClass<actions::StatefulImageActionItem>(
+          action_item)) {
     image_model = static_cast<actions::StatefulImageActionItem*>(action_item)
                       ->GetStatefulImage();
   } else {
diff --git a/chrome/browser/ui/views/toolbar/pinned_action_toolbar_button_menu_model.cc b/chrome/browser/ui/views/toolbar/pinned_action_toolbar_button_menu_model.cc
index 297ed27..b353b66 100644
--- a/chrome/browser/ui/views/toolbar/pinned_action_toolbar_button_menu_model.cc
+++ b/chrome/browser/ui/views/toolbar/pinned_action_toolbar_button_menu_model.cc
@@ -23,7 +23,6 @@
 #include "components/prefs/pref_service.h"
 #include "components/tabs/public/tab_interface.h"
 #include "ui/actions/action_id.h"
-#include "ui/actions/action_utils.h"
 #include "ui/actions/actions.h"
 #include "ui/menus/simple_menu_model.h"
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_controller.cc b/chrome/browser/ui/views/toolbar/toolbar_controller.cc
index b3cb547..0b5c289 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_controller.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_controller.cc
@@ -31,6 +31,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/vector_icons/vector_icons.h"
 #include "third_party/abseil-cpp/absl/functional/overload.h"
+#include "ui/actions/actions.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/mojom/menu_source_type.mojom.h"
 #include "ui/gfx/paint_vector_icon.h"
diff --git a/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.cc b/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.cc
index a72c8d7..ea08cd4 100644
--- a/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.cc
+++ b/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.cc
@@ -145,8 +145,9 @@
   std::unique_ptr<ui::DialogModel> dialog_model =
       CreateProtocolHandlerPickerDialog(
           protocol_url, app_entries, initiator_origin,
-          base::BindOnce(&ProtocolHandlerPickerCoordinator::OnPickerClosed,
-                         weak_factory_.GetWeakPtr(), protocol_url));
+          base::BindOnce(
+              &ProtocolHandlerPickerCoordinator::OnPreferredHandlerSelected,
+              weak_factory_.GetWeakPtr(), protocol_url));
 
   views::BubbleDialogModelHost* model_host =
       views::BubbleDialogModelHost::CreateModal(std::move(dialog_model),
@@ -176,15 +177,12 @@
   return dialog_ && !dialog_->IsClosed();
 }
 
-void ProtocolHandlerPickerCoordinator::OnPickerClosed(
+void ProtocolHandlerPickerCoordinator::OnPreferredHandlerSelected(
     const GURL& protocol_url,
-    std::optional<ProtocolHandlerPickerDialogResult> result) {
-  if (!result) {
-    return;
-  }
-  const auto& app_id = result->selected_app_id;
-  if (result->remember_choice) {
-    proxy_->SetProtocolLinkPreference(app_id, protocol_url.GetScheme());
+    const std::string& app_id,
+    bool remember_choice) {
+  if (remember_choice) {
+    proxy_->SetProtocolLinkPreference(app_id, protocol_url.scheme());
   }
   LaunchApp(protocol_url, app_id);
 }
diff --git a/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.h b/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.h
index 3b3a143..59b3364 100644
--- a/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.h
+++ b/chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.h
@@ -50,8 +50,9 @@
       const GURL& protocol_url,
       const std::optional<url::Origin>& initiating_origin,
       ProtocolHandlerPickerDialogEntries app_entries);
-  void OnPickerClosed(const GURL& protocol_url,
-                      std::optional<ProtocolHandlerPickerDialogResult> result);
+  void OnPreferredHandlerSelected(const GURL& protocol_url,
+                                  const std::string& app_id,
+                                  bool remember_selection);
   bool HasOpenDialogWidget() const;
   void CloseDialogWidget(views::Widget::ClosedReason reason);
 
diff --git a/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.cc b/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.cc
index b6b622d..50239b1 100644
--- a/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.cc
+++ b/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.cc
@@ -8,27 +8,69 @@
 
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
-#include "base/strings/utf_string_conversions.h"
+#include "base/memory/raw_ptr.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/constrained_window/constrained_window_views.h"
 #include "components/strings/grit/components_strings.h"
+#include "components/vector_icons/vector_icons.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/models/dialog_model.h"
 #include "ui/base/models/dialog_model_field.h"
+#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
 #include "ui/gfx/text_constants.h"
 #include "ui/gfx/text_elider.h"
+#include "ui/strings/grit/ui_strings.h"
+#include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/background.h"
+#include "ui/views/bubble/bubble_dialog_model_host.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/checkbox.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/scroll_view.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/layout_provider.h"
+#include "ui/views/layout/layout_types.h"
+#include "ui/views/metadata/view_factory_internal.h"
+#include "ui/views/vector_icons.h"
+#include "ui/views/view_class_properties.h"
 #include "url/gurl.h"
-#include "url/url_constants.h"
 
 DEFINE_ELEMENT_IDENTIFIER_VALUE(
     kProtocolHandlerPickerDialogRememberSelectionCheckboxId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kProtocolHandlerPickerDialogOkButtonId);
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kProtocolHandlerPickerDialogSelectionId);
 
+namespace web_app {
 namespace {
 
+constexpr int kAppNameLabelHeight = 20;
+
+constexpr int kCheckIconSize = 20;
+
+enum class BackgroundType { kDefault, kSelected, kHovered };
+
+std::unique_ptr<views::Background> CreateBackgroundForProtocolHandlerRow(
+    BackgroundType type) {
+  auto token = [&] {
+    switch (type) {
+      case BackgroundType::kDefault:
+        return cros_tokens::kCrosSysAppBaseShaded;
+      case BackgroundType::kSelected:
+        return cros_tokens::kCrosSysSystemPrimaryContainer;
+      case BackgroundType::kHovered:
+        return cros_tokens::kCrosSysHoverOnSubtle;
+    }
+  }();
+
+  return views::CreateRoundedRectBackground(
+      token, views::LayoutProvider::Get()->GetCornerRadiusMetric(
+                 views::ShapeContextTokens::kDialogRadius));
+}
+
 std::u16string GetDialogTitle(
     const GURL& protocol_url,
     const web_app::ProtocolHandlerPickerDialogEntries& apps) {
@@ -56,34 +98,279 @@
                    IDS_PROTOCOL_HANDLER_PICKER_PARAGRAPH_GENERIC);
 }
 
+class SelectionView : public views::ScrollView,
+                      public ProtocolHandlerPickerSelectionRowView::Delegate {
+  METADATA_HEADER(SelectionView, views::ScrollView)
+
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+    virtual void OnSelected(const std::string& app_id) = 0;
+  };
+
+  SelectionView(const ProtocolHandlerPickerDialogEntries& apps,
+                Delegate& delegate);
+  ~SelectionView() override = default;
+
+ private:
+  void OnRowClicked(ProtocolHandlerPickerSelectionRowView* row) override;
+
+  static constexpr int kProtocolHandlerRowGroupID = 1;
+
+  raw_ptr<ProtocolHandlerPickerSelectionRowView> selected_row_ = nullptr;
+  const raw_ref<Delegate> delegate_;
+};
+
+SelectionView::SelectionView(const ProtocolHandlerPickerDialogEntries& apps,
+                             Delegate& delegate)
+    : delegate_(delegate) {
+  auto* scrollable_container = SetContents(std::make_unique<views::View>());
+  scrollable_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical, gfx::Insets(),
+      views::LayoutProvider::Get()->GetDistanceMetric(
+          views::DISTANCE_RELATED_CONTROL_VERTICAL)));
+
+  scrollable_container->SetProperty(views::kElementIdentifierKey,
+                                    kProtocolHandlerPickerDialogSelectionId);
+  if (apps.size() == 1) {
+    // For the single-app case, the app is already pre-selected for the user
+    // (and has no check icon).
+    auto* row = scrollable_container->AddChildView(
+        std::make_unique<ProtocolHandlerPickerSelectionRowView>(
+            apps[0], *this, /*include_check_icon=*/false));
+    row->SetGroup(kProtocolHandlerRowGroupID);
+    row->SetSelected(true);
+    selected_row_ = row;
+  } else {
+    for (const auto& app : apps) {
+      auto* app_row = scrollable_container->AddChildView(
+          std::make_unique<ProtocolHandlerPickerSelectionRowView>(app, *this));
+      app_row->SetGroup(kProtocolHandlerRowGroupID);
+    }
+  }
+
+  // For <= 3 apps, no scrolling happens; for >= 4 apps, the fourth and
+  // further apps will be placed in a cropped scroll view (note that the
+  // fourth app won't be fully visible due to the 8px gap between app
+  // entries).
+  const int row_height =
+      contents()->children().front()->GetPreferredSize().height();
+  ClipHeightTo(row_height, 4 * row_height);
+}
+
+void SelectionView::OnRowClicked(ProtocolHandlerPickerSelectionRowView* row) {
+  if (selected_row_ == row) {
+    return;
+  }
+  if (selected_row_) {
+    // Unselect previously selected row.
+    selected_row_->SetSelected(false);
+  }
+  selected_row_ = row;
+  selected_row_->SetSelected(true);
+  // Select the new row and inform the delegate.
+  delegate_->OnSelected(selected_row_->app_id());
+}
+
+BEGIN_METADATA(SelectionView)
+END_METADATA
+
+class ProtocolHandlerPickerDelegate : public ui::DialogModelDelegate,
+                                      public SelectionView::Delegate {
+ public:
+  explicit ProtocolHandlerPickerDelegate(OnPreferredHandlerSelected callback)
+      : callback_(std::move(callback)) {}
+
+  ~ProtocolHandlerPickerDelegate() override = default;
+
+  void OnSelected(const std::string& app_id) override {
+    selected_app_id_ = app_id;
+    ui::DialogModel::Button* ok_button = dialog_model()->GetButtonByUniqueId(
+        kProtocolHandlerPickerDialogOkButtonId);
+    dialog_model()->SetButtonEnabled(ok_button, true);
+  }
+
+  void OnAccept() {
+    bool remember_choice =
+        dialog_model()
+            ->GetCheckboxByUniqueId(
+                kProtocolHandlerPickerDialogRememberSelectionCheckboxId)
+            ->is_checked();
+
+    CHECK(selected_app_id_);
+    CHECK(callback_);
+    std::move(callback_).Run(*selected_app_id_, remember_choice);
+  }
+
+ private:
+  std::optional<std::string> selected_app_id_;
+  OnPreferredHandlerSelected callback_;
+};
+
 }  // namespace
 
-namespace web_app {
+ProtocolHandlerPickerSelectionRowView::ProtocolHandlerPickerSelectionRowView(
+    const ProtocolHandlerPickerDialogEntry& app,
+    Delegate& delegate,
+    bool include_check_icon)
+    : Button(base::BindRepeating(
+          &ProtocolHandlerPickerSelectionRowView::OnRowClicked,
+          base::Unretained(this))),
+      app_id_(app.app_id),
+      delegate_(delegate) {
+  auto layout_manager = std::make_unique<views::FlexLayout>();
+  layout_manager->SetOrientation(views::LayoutOrientation::kHorizontal)
+      .SetCrossAxisAlignment(views::LayoutAlignment::kCenter);
+
+  // Defines the left margin for child elements.
+  const auto left_gap =
+      gfx::Insets().set_left(views::LayoutProvider::Get()->GetDistanceMetric(
+          views::DISTANCE_RELATED_CONTROL_HORIZONTAL));
+  auto builder =
+      views::Builder<ProtocolHandlerPickerSelectionRowView>(this)
+          .SetBorder(views::CreateEmptyBorder(
+              views::LayoutProvider::Get()->GetDistanceMetric(
+                  views::DISTANCE_UNRELATED_CONTROL_HORIZONTAL)))
+          .SetLayoutManager(std::move(layout_manager))
+          .AddChildren(
+              // App icon
+              views::Builder<views::ImageView>().SetImage(app.icon),
+              // App label
+              views::Builder<views::Label>()
+                  .SetAccessibleRole(ax::mojom::Role::kSectionHeader)
+                  .SetText(app.app_name)
+                  .SetElideBehavior(gfx::ELIDE_TAIL)
+                  .SetHorizontalAlignment(gfx::ALIGN_LEFT)
+                  .SetProperty(views::kFlexBehaviorKey,
+                               views::FlexSpecification(
+                                   views::LayoutOrientation::kHorizontal,
+                                   views::MinimumFlexSizeRule::kScaleToZero,
+                                   views::MaximumFlexSizeRule::kUnbounded))
+                  .SetFontList(gfx::FontList("Roboto, 14px")
+                                   .DeriveWithWeight(gfx::Font::Weight::MEDIUM))
+                  .SetLineHeight(kAppNameLabelHeight)
+                  .SetProperty(views::kMarginsKey, left_gap))
+          .SetAccessibleRole(ax::mojom::Role::kMenuItemRadio)
+          .SetAccessibleName(app.app_name)
+          .SetCheckedState(ax::mojom::CheckedState::kFalse)
+          .SetInstallFocusRingOnFocus(false)
+          .SetBackground(
+              CreateBackgroundForProtocolHandlerRow(BackgroundType::kDefault));
+
+  if (include_check_icon) {
+    // Check icon
+    builder.AddChild(
+        views::Builder<views::ImageView>()
+            .SetImage(ui::ImageModel::FromVectorIcon(
+                vector_icons::kCheckCircleIcon,
+                cros_tokens::kCrosSysOnPrimaryContainer, kCheckIconSize))
+            .SetVisible(false)
+            .SetProperty(views::kMarginsKey, left_gap)
+            .CopyAddressTo(&check_icon_));
+  }
+
+  std::move(builder).BuildChildren();
+}
+
+ProtocolHandlerPickerSelectionRowView::
+    ~ProtocolHandlerPickerSelectionRowView() = default;
+
+void ProtocolHandlerPickerSelectionRowView::SetSelected(bool selected) {
+  if (is_selected_ == selected) {
+    return;
+  }
+  is_selected_ = selected;
+  if (check_icon_) {
+    check_icon_->SetVisible(is_selected_);
+  }
+  SetCheckedState(is_selected_ ? ax::mojom::CheckedState::kTrue
+                               : ax::mojom::CheckedState::kFalse);
+  UpdateBackground();
+}
+
+bool ProtocolHandlerPickerSelectionRowView::IsSelected() const {
+  return is_selected_;
+}
+
+void ProtocolHandlerPickerSelectionRowView::SetCheckedState(
+    ax::mojom::CheckedState state) {
+  GetViewAccessibility().SetCheckedState(state);
+}
+
+void ProtocolHandlerPickerSelectionRowView::OnRowClicked() {
+  delegate_->OnRowClicked(this);
+}
+
+void ProtocolHandlerPickerSelectionRowView::StateChanged(
+    ButtonState old_state) {
+  UpdateBackground();
+}
+
+void ProtocolHandlerPickerSelectionRowView::UpdateBackground() {
+  // Selection state always takes precedence.
+  if (is_selected_) {
+    SetBackground(
+        CreateBackgroundForProtocolHandlerRow(BackgroundType::kSelected));
+    return;
+  }
+
+  // If not selected, check for hover state.
+  if (GetState() == STATE_HOVERED) {
+    SetBackground(
+        CreateBackgroundForProtocolHandlerRow(BackgroundType::kHovered));
+  } else {
+    SetBackground(
+        CreateBackgroundForProtocolHandlerRow(BackgroundType::kDefault));
+  }
+}
+
+BEGIN_METADATA(ProtocolHandlerPickerSelectionRowView)
+END_METADATA
 
 std::unique_ptr<ui::DialogModel> CreateProtocolHandlerPickerDialog(
     const GURL& protocol_url,
     const ProtocolHandlerPickerDialogEntries& apps,
     const std::optional<url::Origin>& initiator_origin,
-    OnPickerClosedCallback callback) {
-  return ui::DialogModel::Builder()
-      .SetInternalName("ProtocolHandlerPickerDialog")
-      .SetTitle(GetDialogTitle(protocol_url, apps))
-      .AddParagraph(ui::DialogModelLabel(GetDialogParagraph(initiator_origin)))
-      .AddCheckbox(
-          kProtocolHandlerPickerDialogRememberSelectionCheckboxId,
-          ui::DialogModelLabel::CreateWithReplacement(
-              IDS_PROTOCOL_HANDLER_PICKER_DIALOG_ALWAYS_OPEN_IN_THIS_APP,
-              ui::DialogModelLabel::CreatePlainText(
-                  base::UTF8ToUTF16(protocol_url.GetScheme()) +
-                  url::kStandardSchemeSeparator16)))
-      .AddCancelButton(base::DoNothing())
-      .AddOkButton(base::DoNothing(),
-                   ui::DialogModel::Button::Params()
-                       .SetEnabled(true)
-                       .SetId(kProtocolHandlerPickerDialogOkButtonId)
-                       .SetLabel(l10n_util::GetStringUTF16(IDS_OPEN)))
-      .OverrideDefaultButton(ui::mojom::DialogButton::kCancel)
-      .Build();
+    OnPreferredHandlerSelected callback) {
+  CHECK(!apps.empty());
+
+  auto delegate =
+      std::make_unique<ProtocolHandlerPickerDelegate>(std::move(callback));
+  auto* delegate_ptr = delegate.get();
+
+  auto dialog_model =
+      ui::DialogModel::Builder(std::move(delegate))
+          .SetInternalName("ProtocolHandlerPickerDialog")
+          .SetTitle(GetDialogTitle(protocol_url, apps))
+          .AddParagraph(
+              ui::DialogModelLabel(GetDialogParagraph(initiator_origin)))
+          .AddCustomField(
+              std::make_unique<views::BubbleDialogModelHost::CustomView>(
+                  std::make_unique<SelectionView>(apps, *delegate_ptr),
+                  views::BubbleDialogModelHost::FieldType::kControl))
+          .AddCheckbox(
+              kProtocolHandlerPickerDialogRememberSelectionCheckboxId,
+              ui::DialogModelLabel::CreateWithReplacement(
+                  IDS_PROTOCOL_HANDLER_PICKER_DIALOG_ALWAYS_OPEN_IN_THIS_APP,
+                  ui::DialogModelLabel::CreatePlainText(
+                      base::UTF8ToUTF16(protocol_url.scheme()) +
+                      url::kStandardSchemeSeparator16)))
+          .AddCancelButton(base::DoNothing())
+          .AddOkButton(base::BindOnce(&ProtocolHandlerPickerDelegate::OnAccept,
+                                      base::Unretained(delegate_ptr)),
+                       ui::DialogModel::Button::Params()
+                           .SetEnabled(false)
+                           .SetId(kProtocolHandlerPickerDialogOkButtonId)
+                           .SetLabel(l10n_util::GetStringUTF16(IDS_OPEN)))
+          .OverrideDefaultButton(ui::mojom::DialogButton::kCancel)
+          .Build();
+
+  if (apps.size() == 1) {
+    // Enable the button and set the default callback param to the only app id.
+    delegate_ptr->OnSelected(apps[0].app_id);
+  }
+  return dialog_model;
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.h b/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.h
index 1fc9151..8bcb65cf 100644
--- a/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.h
+++ b/chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.h
@@ -15,12 +15,17 @@
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/models/dialog_model.h"
 #include "ui/base/models/image_model.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/scroll_view.h"
+#include "ui/views/metadata/view_factory.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
 DECLARE_ELEMENT_IDENTIFIER_VALUE(
     kProtocolHandlerPickerDialogRememberSelectionCheckboxId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kProtocolHandlerPickerDialogOkButtonId);
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kProtocolHandlerPickerDialogSelectionId);
 
 namespace web_app {
 
@@ -33,23 +38,68 @@
 using ProtocolHandlerPickerDialogEntries =
     std::vector<ProtocolHandlerPickerDialogEntry>;
 
-struct ProtocolHandlerPickerDialogResult {
-  std::string selected_app_id;
-  bool remember_choice;
+// Represents a single row within the scrollable selection.
+// <app-icon> <app-name> <optional-check-icon-when-selected>
+class ProtocolHandlerPickerSelectionRowView : public views::Button {
+  METADATA_HEADER(ProtocolHandlerPickerSelectionRowView, views::Button)
+
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+    virtual void OnRowClicked(ProtocolHandlerPickerSelectionRowView*) = 0;
+  };
+
+  // When `include_check_icon` is false, no check icon is displayed when
+  // selected.
+  ProtocolHandlerPickerSelectionRowView(
+      const ProtocolHandlerPickerDialogEntry& app,
+      Delegate& delegate,
+      bool include_check_icon = true);
+  ~ProtocolHandlerPickerSelectionRowView() override;
+
+  void SetSelected(bool selected);
+  bool IsSelected() const;
+
+  const std::string& app_id() const { return app_id_; }
+
+  // For the ViewBuilder.
+  void SetCheckedState(ax::mojom::CheckedState state);
+
+ private:
+  void OnRowClicked();
+
+  // views::Button:
+  void StateChanged(ButtonState old_state) override;
+
+  void UpdateBackground();
+
+  const std::string app_id_;
+  bool is_selected_ = false;
+  raw_ptr<views::ImageView> check_icon_ = nullptr;
+
+  const raw_ref<Delegate> delegate_;
 };
 
-// The callback to be run when the dialog is closed for any reason.
-// The optional will be empty (std::nullopt) if the user canceled or the dialog
-// was destroyed for some reason.
-using OnPickerClosedCallback =
-    base::OnceCallback<void(std::optional<ProtocolHandlerPickerDialogResult>)>;
+// Represents the selection of potential protocol handlers; it becomes
+// scrollable when there are >3 entries. At most one entry can be in the
+// selected state at a time.
+BEGIN_VIEW_BUILDER(, ProtocolHandlerPickerSelectionRowView, views::Button)
+VIEW_BUILDER_METHOD(SetCheckedState, ax::mojom::CheckedState)
+END_VIEW_BUILDER
+
+// The callback to be run when the dialog is accepted.
+using OnPreferredHandlerSelected =
+    base::OnceCallback<void(const std::string& app_id, bool remember_choice)>;
 
 std::unique_ptr<ui::DialogModel> CreateProtocolHandlerPickerDialog(
     const GURL& protocol_url,
     const ProtocolHandlerPickerDialogEntries& apps,
     const std::optional<url::Origin>& initiator_origin,
-    OnPickerClosedCallback callback);
+    OnPreferredHandlerSelected callback);
 
 }  // namespace web_app
 
+DEFINE_VIEW_BUILDER(, web_app::ProtocolHandlerPickerSelectionRowView)
+
 #endif  // CHROME_BROWSER_UI_VIEWS_WEB_APPS_PROTOCOL_HANDLER_PICKER_DIALOG_H_
diff --git a/chrome/browser/ui/views/web_apps/protocol_handler_picker_interactve_uitest.cc b/chrome/browser/ui/views/web_apps/protocol_handler_picker_interactve_uitest.cc
new file mode 100644
index 0000000..1912b07
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/protocol_handler_picker_interactve_uitest.cc
@@ -0,0 +1,289 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_WEB_APPS_PROTOCOL_HANDLER_PICKER_INTERACTVE_UITEST_CC_
+#define CHROME_BROWSER_UI_VIEWS_WEB_APPS_PROTOCOL_HANDLER_PICKER_INTERACTVE_UITEST_CC_
+
+#include "base/test/run_until.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_ash.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/external_protocol/external_protocol_handler.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/views/web_apps/protocol_handler_picker_dialog.h"
+#include "chrome/browser/ui/web_applications/app_browser_controller.h"
+#include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
+#include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "chrome/test/interaction/interactive_browser_test.h"
+#include "chromeos/constants/chromeos_features.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "ui/base/window_open_disposition.h"
+
+namespace web_app {
+
+namespace {
+
+std::string Tag(uint32_t i) {
+  return base::StringPrintf("row_%i", i);
+}
+
+}  // namespace
+
+class ProtocolHandlerPickerUITest
+    : public InteractiveBrowserTestT<IsolatedWebAppBrowserTestHarness> {
+ public:
+  webapps::AppId InstallIwaAndAllowProtocolLinkHandling() {
+    auto app_id = web_app::IsolatedWebAppBuilder(
+                      web_app::ManifestBuilder()
+                          .SetName("app-1.0.0")
+                          .SetVersion("1.0.0")
+                          .AddProtocolHandler("meow", "/index.html?params=%s"))
+                      .AddHtml("/index.html", "<html></html>")
+                      .BuildBundle()
+                      ->InstallChecked(profile())
+                      .app_id();
+    base::test::TestFuture<void> future;
+    web_app::WebAppProvider::GetForWebApps(profile())
+        ->scheduler()
+        .UpdateProtocolHandlerUserApproval(app_id, "meow",
+                                           web_app::ApiApprovalState::kAllowed,
+                                           future.GetCallback());
+    EXPECT_TRUE(future.Wait());
+    return app_id;
+  }
+
+  void NavigateCurrentTabToAboutBlank() {
+    ui_test_utils::NavigateToURLWithDisposition(
+        browser(), GURL("about:blank"), WindowOpenDisposition::CURRENT_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  }
+
+  auto LaunchProtocolLink(bool in_new_tab) {
+    return Do([&, in_new_tab] {
+      ASSERT_THAT(
+          content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
+                          base::StringPrintf("window.open('meow://link', '%s')",
+                                             in_new_tab ? "_blank" : "_self")),
+          content::EvalJsResult::IsOk());
+    });
+  }
+
+  auto WaitForAppWindow(const webapps::AppId& app_id) {
+    DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ui::test::PollingStateObserver<bool>,
+                                        kAppWindowOpened);
+    return Steps(PollState(kAppWindowOpened,
+                           [&, app_id]() {
+                             return !!AppBrowserController::FindForWebApp(
+                                 *profile(), app_id);
+                           }),
+                 WaitForState(kAppWindowOpened, true),
+                 StopObservingState(kAppWindowOpened));
+  }
+
+  auto WaitForAppWindow() {
+    DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ui::test::PollingStateObserver<bool>,
+                                        kAppWindowOpened);
+    return Steps(PollState(kAppWindowOpened,
+                           [&]() {
+                             return !!AppBrowserController::FindForWebApp(
+                                 *profile(), *selected_app_id_);
+                           }),
+                 WaitForState(kAppWindowOpened, true),
+                 StopObservingState(kAppWindowOpened));
+  }
+
+  auto WaitForOpenButton() {
+    return WaitForShow(kProtocolHandlerPickerDialogOkButtonId);
+  }
+
+  auto ClickOpenButton() {
+    return PressButton(kProtocolHandlerPickerDialogOkButtonId);
+  }
+
+  auto WaitForDialogHide() {
+    // The dialog will always have an ok button, and if the ok button is hidden
+    // the dialog is hidden as well.
+    return WaitForHide(kProtocolHandlerPickerDialogOkButtonId);
+  }
+
+  auto RememberProtocolSelection() {
+    return PressButton(kProtocolHandlerPickerDialogRememberSelectionCheckboxId);
+  }
+
+  auto WaitForProtocolsLoaded() {
+    uint32_t num_apps = apps::AppServiceProxyFactory::GetForProfile(profile())
+                            ->GetAppIdsForUrl(GURL("meow://"))
+                            .size();
+    auto steps = Steps(WaitForShow(kProtocolHandlerPickerDialogSelectionId));
+    for (uint32_t i = 0; i < num_apps; i++) {
+      steps.push_back(
+          NameDescendantViewByType<ProtocolHandlerPickerSelectionRowView>(
+              kProtocolHandlerPickerDialogSelectionId, Tag(i), i));
+    }
+    return InSameContext(std::move(steps));
+  }
+
+  auto SelectProtocolHandler(uint32_t index) {
+    return InSameContext(
+        PressButton(Tag(index)),
+        WithView(Tag(index), [&](ProtocolHandlerPickerSelectionRowView* view) {
+          selected_app_id_ = view->app_id();
+        }));
+  }
+
+  auto CheckOpenButtonEnabled(bool enabled) {
+    return CheckViewProperty(kProtocolHandlerPickerDialogOkButtonId,
+                             &views::View::GetEnabled, enabled);
+  }
+
+  auto CheckProtocolHandlerSelected(uint32_t index, bool selected) {
+    return CheckViewProperty(Tag(index),
+                             &ProtocolHandlerPickerSelectionRowView::IsSelected,
+                             selected);
+  }
+
+  auto CheckProtocolSelectionRemembered() {
+    return InSameContext(
+        Check([&] { return selected_app_id_.has_value(); }, "No app selected!"),
+        Check(
+            [&] {
+              return apps::AppServiceProxyFactory::GetForProfile(profile())
+                         ->PreferredAppsList()
+                         .FindPreferredAppForUrl(GURL("meow://")) ==
+                     *selected_app_id_;
+            },
+            "No preference was set!"));
+  }
+
+  // Waits will the default browser gets exactly X tabs.
+  auto WaitForTabCount(uint32_t count) {
+    DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ui::test::PollingStateObserver<int>,
+                                        kTabCountState);
+    return Steps(
+        PollState(kTabCountState,
+                  [&]() { return browser()->tab_strip_model()->count(); }),
+        WaitForState(kTabCountState, count),
+        StopObservingState(kTabCountState));
+  }
+
+ private:
+  std::optional<webapps::AppId> selected_app_id_;
+  base::test::ScopedFeatureList features_{
+      chromeos::features::kWebAppManifestProtocolHandlerSupport};
+};
+
+IN_PROC_BROWSER_TEST_F(ProtocolHandlerPickerUITest, AcceptForOneApp) {
+  NavigateCurrentTabToAboutBlank();
+  webapps::AppId app_id = InstallIwaAndAllowProtocolLinkHandling();
+
+  RunTestSequence(
+      // clang-format off
+    // There's currently one open tab.
+    WaitForTabCount(1),
+
+    // Trigger the flow.
+    Do(&ExternalProtocolHandler::PermitLaunchUrl),
+    LaunchProtocolLink(/*in_new_tab=*/false),
+
+    // Wait for the dialog to show up in the same tab.
+    WaitForOpenButton(),
+    CheckOpenButtonEnabled(true),
+    WaitForTabCount(1),
+
+    // Apply selection. This hides the dialog and spawns a new app window; the
+    // number of tabs in the default browser stays the same.
+    ClickOpenButton(),
+    WaitForDialogHide(),
+    WaitForAppWindow(app_id),
+    WaitForTabCount(1)
+      // clang-format on
+  );
+}
+
+IN_PROC_BROWSER_TEST_F(ProtocolHandlerPickerUITest, AcceptForOneAppInNewTab) {
+  NavigateCurrentTabToAboutBlank();
+  webapps::AppId app_id = InstallIwaAndAllowProtocolLinkHandling();
+
+  RunTestSequence(
+      // clang-format off
+    // There's currently one open tab.
+    WaitForTabCount(1),
+
+    // Trigger the flow.
+    Do(&ExternalProtocolHandler::PermitLaunchUrl),
+    LaunchProtocolLink(/*in_new_tab=*/true),
+
+    // Wait for the dialog to show up in a new tab.
+    WaitForOpenButton(),
+    CheckOpenButtonEnabled(true),
+    WaitForTabCount(2),
+
+    // Apply selection. This hides the dialog and spawns a new app window; the
+    // number of tabs in the default browser must decrease by one.
+    ClickOpenButton(),
+    WaitForDialogHide(),
+    WaitForAppWindow(app_id),
+    WaitForTabCount(1)
+      // clang-format on
+  );
+}
+
+IN_PROC_BROWSER_TEST_F(ProtocolHandlerPickerUITest, AcceptForTwoApps) {
+  NavigateCurrentTabToAboutBlank();
+
+  // Install two different apps.
+  InstallIwaAndAllowProtocolLinkHandling();
+  InstallIwaAndAllowProtocolLinkHandling();
+
+  RunTestSequence(
+      // clang-format off
+    // There's currently one open tab.
+    WaitForTabCount(1),
+
+    // Trigger the flow.
+    Do(&ExternalProtocolHandler::PermitLaunchUrl),
+    LaunchProtocolLink(/*in_new_tab=*/true),
+
+    // Wait for the dialog to show up.
+    // Originally the button must be disabled.
+    WaitForProtocolsLoaded(),
+    WaitForOpenButton(),
+    CheckOpenButtonEnabled(false),
+    WaitForTabCount(2),
+
+    // Select the first entry; this enables the open button.
+    SelectProtocolHandler(0),
+    CheckProtocolHandlerSelected(0, true),
+    CheckProtocolHandlerSelected(1, false),
+    CheckOpenButtonEnabled(true),
+
+    // Change the selection to second entry; this resets selection on the first
+    // entry, but keeps the open button enabled.
+    SelectProtocolHandler(1),
+    CheckProtocolHandlerSelected(0, false),
+    CheckProtocolHandlerSelected(1, true),
+    CheckOpenButtonEnabled(true),
+
+    // Also apply the "remember" selection.
+    RememberProtocolSelection(),
+
+    // Apply selection. This hides the dialog and spawns a new app window; the
+    // number of tabs in the default browser must decrease by one.
+    ClickOpenButton(),
+    WaitForDialogHide(),
+    WaitForAppWindow(),
+    WaitForTabCount(1),
+
+    // Also ensure that the selection has been persisted.
+    CheckProtocolSelectionRemembered()
+      // clang-format on
+  );
+}
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WEB_APPS_PROTOCOL_HANDLER_PICKER_INTERACTVE_UITEST_CC_
diff --git a/chrome/browser/ui/views/web_apps/web_app_navigation_capturing_iph_interactive_uitest.cc b/chrome/browser/ui/views/web_apps/web_app_navigation_capturing_iph_interactive_uitest.cc
index ceaeb92..891ed313 100644
--- a/chrome/browser/ui/views/web_apps/web_app_navigation_capturing_iph_interactive_uitest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_navigation_capturing_iph_interactive_uitest.cc
@@ -137,7 +137,15 @@
   auto OpenApp(const webapps::AppId& app_id) {
     auto steps =
         Steps(InstrumentNextTab(kAppPageId, AnyBrowser()), Do([this, app_id]() {
-                web_app::LaunchWebAppBrowser(browser()->profile(), app_id);
+                web_app::WebAppProvider* provider =
+                    web_app::WebAppProvider::GetForLocalAppsUnchecked(
+                        browser()->profile());
+                provider->scheduler().LaunchAppWithCustomParams(
+                    apps::AppLaunchParams(
+                        app_id, apps::LaunchContainer::kLaunchContainerWindow,
+                        WindowOpenDisposition::CURRENT_TAB,
+                        apps::LaunchSource::kFromTest),
+                    base::DoNothing());
               }),
               InAnyContext(WaitForShow(kAppPageId)));
     AddDescriptionPrefix(steps, base::StrCat({"OpenApp( ", app_id, " )"}));
diff --git a/chrome/browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc
index 689cb28..0226f4e 100644
--- a/chrome/browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc
@@ -6,12 +6,10 @@
 #include "base/strings/string_util.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/test_future.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/apps/link_capturing/link_capturing_feature_test_support.h"
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/themes/theme_service.h"
@@ -283,13 +281,18 @@
   webapps::AppId app_id = Install();
 
   // Trigger the launch but do not wait for the web contents to load.
-  content::WebContents* web_contents =
-      apps::AppServiceProxyFactory::GetForProfile(profile())
-          ->BrowserAppLauncher()
-          ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
-              app_id, apps::LaunchContainer::kLaunchContainerWindow,
-              WindowOpenDisposition::CURRENT_TAB,
-              apps::LaunchSource::kFromTest));
+  web_app::WebAppProvider* provider =
+      web_app::WebAppProvider::GetForLocalAppsUnchecked(profile());
+  base::test::TestFuture<base::WeakPtr<Browser>,
+                         base::WeakPtr<content::WebContents>,
+                         apps::LaunchContainer>
+      future;
+  provider->scheduler().LaunchAppWithCustomParams(
+      apps::AppLaunchParams(
+          app_id, apps::LaunchContainer::kLaunchContainerWindow,
+          WindowOpenDisposition::CURRENT_TAB, apps::LaunchSource::kFromTest),
+      future.GetCallback());
+  content::WebContents* web_contents = future.template Get<1>().get();
   ASSERT_TRUE(web_contents);
   Browser* app_browser = chrome::FindBrowserWithTab(web_contents);
   App app{app_id, app_browser,
diff --git a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
index 90cf440..26341aed 100644
--- a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
+++ b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
@@ -22,11 +22,11 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/test/bind.h"
 #include "base/test/gtest_util.h"
+#include "base/test/test_future.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/ash/app_list/app_service/app_service_app_item.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
@@ -54,6 +54,7 @@
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
 #include "chrome/browser/web_applications/web_app_command_manager.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_tab_helper.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
@@ -652,27 +653,35 @@
           ProfileMetrics::DELETE_PROFILE_USER_MANAGER);
 
   {
-    auto launch_params = apps::AppLaunchParams(
-        app_id2, apps::LaunchContainer::kLaunchContainerWindow,
-        WindowOpenDisposition::CURRENT_TAB,
-        apps::LaunchSource::kFromAppListGrid);
-    content::WebContents* web_contents =
-        apps::AppServiceProxyFactory::GetForProfile(profile2)
-            ->BrowserAppLauncher()
-            ->LaunchAppWithParamsForTesting(std::move(launch_params));
-    EXPECT_EQ(web_contents, nullptr);
+    web_app::WebAppProvider* provider =
+        web_app::WebAppProvider::GetForLocalAppsUnchecked(profile2);
+    base::test::TestFuture<base::WeakPtr<Browser>,
+                           base::WeakPtr<content::WebContents>,
+                           apps::LaunchContainer>
+        future;
+    provider->scheduler().LaunchAppWithCustomParams(
+        apps::AppLaunchParams(app_id2,
+                              apps::LaunchContainer::kLaunchContainerWindow,
+                              WindowOpenDisposition::CURRENT_TAB,
+                              apps::LaunchSource::kFromAppListGrid),
+        future.GetCallback());
+    EXPECT_FALSE(future.template Get<1>().get());
   }
 
   {
-    auto launch_params = apps::AppLaunchParams(
-        app_id1, apps::LaunchContainer::kLaunchContainerWindow,
-        WindowOpenDisposition::CURRENT_TAB,
-        apps::LaunchSource::kFromAppListGrid);
-    content::WebContents* web_contents =
-        apps::AppServiceProxyFactory::GetForProfile(profile1)
-            ->BrowserAppLauncher()
-            ->LaunchAppWithParamsForTesting(std::move(launch_params));
-    EXPECT_NE(web_contents, nullptr);
+    web_app::WebAppProvider* provider =
+        web_app::WebAppProvider::GetForLocalAppsUnchecked(profile1);
+    base::test::TestFuture<base::WeakPtr<Browser>,
+                           base::WeakPtr<content::WebContents>,
+                           apps::LaunchContainer>
+        future;
+    provider->scheduler().LaunchAppWithCustomParams(
+        apps::AppLaunchParams(app_id1,
+                              apps::LaunchContainer::kLaunchContainerWindow,
+                              WindowOpenDisposition::CURRENT_TAB,
+                              apps::LaunchSource::kFromAppListGrid),
+        future.GetCallback());
+    EXPECT_TRUE(future.template Get<1>().get());
   }
 }
 
diff --git a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
index 2986228..582a914 100644
--- a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
+++ b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
@@ -22,9 +22,6 @@
 #include "build/build_config.h"
 #include "build/buildflag.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -217,13 +214,19 @@
   SCOPED_TRACE(base::StrCat({"Attempted to launch ", app_id, " at ",
                              start_url.possibly_invalid_spec(), " with scope ",
                              scope.possibly_invalid_spec()}));
-  content::WebContents* web_contents =
-      apps::AppServiceProxyFactory::GetForProfile(profile)
-          ->BrowserAppLauncher()
-          ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
-              app_id, apps::LaunchContainer::kLaunchContainerWindow,
-              disposition, apps::LaunchSource::kFromTest));
 
+  web_app::WebAppProvider* provider =
+      web_app::WebAppProvider::GetForLocalAppsUnchecked(profile);
+  base::test::TestFuture<base::WeakPtr<Browser>,
+                         base::WeakPtr<content::WebContents>,
+                         apps::LaunchContainer>
+      future;
+  provider->scheduler().LaunchAppWithCustomParams(
+      apps::AppLaunchParams(app_id,
+                            apps::LaunchContainer::kLaunchContainerWindow,
+                            disposition, apps::LaunchSource::kFromTest),
+      future.GetCallback());
+  content::WebContents* web_contents = future.template Get<1>().get();
   if (!web_contents) {
     return nullptr;
   }
@@ -281,13 +284,17 @@
                              start_url.possibly_invalid_spec(), " with scope ",
                              scope.possibly_invalid_spec()}));
 
-  content::WebContents* web_contents =
-      apps::AppServiceProxyFactory::GetForProfile(profile)
-          ->BrowserAppLauncher()
-          ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
-              app_id, apps::LaunchContainer::kLaunchContainerTab, disposition,
-              apps::LaunchSource::kFromTest));
-
+  web_app::WebAppProvider* provider =
+      web_app::WebAppProvider::GetForLocalAppsUnchecked(profile);
+  base::test::TestFuture<base::WeakPtr<Browser>,
+                         base::WeakPtr<content::WebContents>,
+                         apps::LaunchContainer>
+      future;
+  provider->scheduler().LaunchAppWithCustomParams(
+      apps::AppLaunchParams(app_id, apps::LaunchContainer::kLaunchContainerTab,
+                            disposition, apps::LaunchSource::kFromTest),
+      future.GetCallback());
+  content::WebContents* web_contents = future.template Get<1>().get();
   if (!web_contents) {
     return nullptr;
   }
@@ -329,10 +336,16 @@
                                WindowOpenDisposition::NEW_FOREGROUND_TAB,
                                apps::LaunchSource::kFromCommandLine);
   params.override_url = url;
-  content::WebContents* const web_contents =
-      apps::AppServiceProxyFactory::GetForProfile(profile)
-          ->BrowserAppLauncher()
-          ->LaunchAppWithParamsForTesting(std::move(params));
+
+  web_app::WebAppProvider* provider =
+      web_app::WebAppProvider::GetForLocalAppsUnchecked(profile);
+  base::test::TestFuture<base::WeakPtr<Browser>,
+                         base::WeakPtr<content::WebContents>,
+                         apps::LaunchContainer>
+      future;
+  provider->scheduler().LaunchAppWithCustomParams(std::move(params),
+                                                  future.GetCallback());
+  content::WebContents* web_contents = future.template Get<1>().get();
   EXPECT_TRUE(web_contents);
 
   Browser* browser = chrome::FindBrowserWithTab(web_contents);
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 47a81382..01b1cf7 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -32,9 +32,6 @@
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/banners/app_banner_manager_desktop.h"
 #include "chrome/browser/browser_features.h"
 #include "chrome/browser/devtools/protocol/browser_handler.h"
@@ -2685,11 +2682,15 @@
   handler.Close();
   ui_test_utils::WaitForBrowserToClose();
 
-  content::WebContents* const web_contents =
-      apps::AppServiceProxyFactory::GetForProfile(profile)
-          ->BrowserAppLauncher()
-          ->LaunchAppWithParamsForTesting(std::move(params));
-  EXPECT_EQ(web_contents, nullptr);
+  web_app::WebAppProvider* provider =
+      web_app::WebAppProvider::GetForLocalAppsUnchecked(profile);
+  base::test::TestFuture<base::WeakPtr<Browser>,
+                         base::WeakPtr<content::WebContents>,
+                         apps::LaunchContainer>
+      future;
+  provider->scheduler().LaunchAppWithCustomParams(std::move(params),
+                                                  future.GetCallback());
+  EXPECT_FALSE(future.template Get<1>().get());
 }
 
 using WebAppBrowserTest_ManifestId = WebAppBrowserTest;
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest_base.cc b/chrome/browser/ui/web_applications/web_app_browsertest_base.cc
index 6b16cf0cd..d6be6cb 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest_base.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest_base.cc
@@ -10,11 +10,9 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
+#include "base/test/test_future.h"
 #include "base/time/time.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/banners/test_app_banner_manager_desktop.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -29,6 +27,7 @@
 #include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_callback_app_identity.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_ui_manager.h"
@@ -189,15 +188,19 @@
   ui_test_utils::UrlLoadObserver url_observer(
       provider().registrar_unsafe().GetAppStartUrl(app_id));
 
-  apps::AppLaunchParams params(
-      app_id, apps::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
-  content::WebContents* contents =
-      apps::AppServiceProxyFactory::GetForProfile(profile())
-          ->BrowserAppLauncher()
-          ->LaunchAppWithParamsForTesting(std::move(params));
+  web_app::WebAppProvider* provider =
+      web_app::WebAppProvider::GetForLocalAppsUnchecked(profile());
+  base::test::TestFuture<base::WeakPtr<Browser>,
+                         base::WeakPtr<content::WebContents>,
+                         apps::LaunchContainer>
+      future;
+  provider->scheduler().LaunchAppWithCustomParams(
+      apps::AppLaunchParams(
+          app_id, apps::LaunchContainer::kLaunchContainerWindow,
+          WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest),
+      future.GetCallback());
   url_observer.Wait();
-  return contents;
+  return future.template Get<1>().get();
 }
 
 GURL WebAppBrowserTestBase::GetInstallableAppURL() {
diff --git a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
index cb5871b2..d40645ee 100644
--- a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
@@ -11,13 +11,11 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/test_future.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -31,6 +29,7 @@
 #include "chrome/browser/web_applications/os_integration/web_app_file_handler_manager.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
@@ -249,11 +248,16 @@
 
     content::TestNavigationObserver navigation_observer(expected_launch_url);
     navigation_observer.StartWatchingNewWebContents();
-    content::WebContents* web_contents =
-        apps::AppServiceProxyFactory::GetForProfile(profile)
-            ->BrowserAppLauncher()
-            ->LaunchAppWithParamsForTesting(std::move(params));
 
+    web_app::WebAppProvider* provider =
+        web_app::WebAppProvider::GetForLocalAppsUnchecked(profile);
+    base::test::TestFuture<base::WeakPtr<Browser>,
+                           base::WeakPtr<content::WebContents>,
+                           apps::LaunchContainer>
+        future;
+    provider->scheduler().LaunchAppWithCustomParams(std::move(params),
+                                                    future.GetCallback());
+    auto* web_contents = future.template Get<1>().get();
     navigation_observer.Wait();
     AttachTestConsumer(web_contents);
     return web_contents;
diff --git a/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc b/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc
index 286d73e3..99b170c 100644
--- a/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc
+++ b/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc
@@ -5,14 +5,13 @@
 #include "ash/constants/ash_switches.h"
 #include "ash/constants/web_app_id_constants.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -46,11 +45,18 @@
       ash::kOsSettingsAppId, apps::LaunchContainer::kLaunchContainerWindow,
       WindowOpenDisposition::NEW_FOREGROUND_TAB, apps::LaunchSource::kFromTest);
 
-  content::WebContents* contents =
-      apps::AppServiceProxyFactory::GetForProfile(profile)
-          ->BrowserAppLauncher()
-          ->LaunchAppWithParamsForTesting(std::move(params));
-  EXPECT_EQ(GURL(chrome::kChromeUIOSSettingsURL), contents->GetVisibleURL());
+  web_app::WebAppProvider* provider =
+      web_app::WebAppProvider::GetForLocalAppsUnchecked(profile);
+  base::test::TestFuture<base::WeakPtr<Browser>,
+                         base::WeakPtr<content::WebContents>,
+                         apps::LaunchContainer>
+      future;
+  provider->scheduler().LaunchAppWithCustomParams(std::move(params),
+                                                  future.GetCallback());
+  auto* web_contents = future.template Get<1>().get();
+  ASSERT_TRUE(web_contents);
+  EXPECT_EQ(GURL(chrome::kChromeUIOSSettingsURL),
+            web_contents->GetVisibleURL());
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/ui/web_applications/web_app_uninstall_browsertest.cc b/chrome/browser/ui/web_applications/web_app_uninstall_browsertest.cc
index e46245c..5d2926c5 100644
--- a/chrome/browser/ui/web_applications/web_app_uninstall_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_uninstall_browsertest.cc
@@ -6,9 +6,6 @@
 #include "base/test/bind.h"
 #include "base/test/test_future.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
 #include "chrome/browser/ui/browser.h"
@@ -138,11 +135,16 @@
       WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
 
   UninstallWebApp(app_id);
-  content::WebContents* const web_contents =
-      apps::AppServiceProxyFactory::GetForProfile(profile())
-          ->BrowserAppLauncher()
-          ->LaunchAppWithParamsForTesting(std::move(params));
-  EXPECT_EQ(web_contents, nullptr);
+
+  web_app::WebAppProvider* provider =
+      web_app::WebAppProvider::GetForLocalAppsUnchecked(profile());
+  base::test::TestFuture<base::WeakPtr<Browser>,
+                         base::WeakPtr<content::WebContents>,
+                         apps::LaunchContainer>
+      future;
+  provider->scheduler().LaunchAppWithCustomParams(std::move(params),
+                                                  future.GetCallback());
+  EXPECT_FALSE(future.template Get<1>().get());
 }
 
 IN_PROC_BROWSER_TEST_F(WebAppUninstallBrowserTest, TwoUninstallCalls) {
diff --git a/chrome/browser/ui/web_applications/web_share_target_browsertest.cc b/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
index ce5e636..6549e1e 100644
--- a/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-
 #include <limits>
 #include <string>
 #include <string_view>
@@ -16,10 +15,8 @@
 #include "base/memory/raw_ptr.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
+#include "base/test/test_future.h"
 #include "base/threading/thread_restrictions.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/apps/app_service/intent_util.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/apps/link_capturing/link_capturing_feature_test_support.h"
@@ -34,6 +31,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -91,12 +89,15 @@
       display::kDefaultDisplayId, apps::LaunchContainer::kLaunchContainerWindow,
       std::move(intent), profile);
 
-  content::WebContents* const web_contents =
-      apps::AppServiceProxyFactory::GetForProfile(profile)
-          ->BrowserAppLauncher()
-          ->LaunchAppWithParamsForTesting(std::move(params));
-  DCHECK(web_contents);
-  return web_contents;
+  web_app::WebAppProvider* provider =
+      web_app::WebAppProvider::GetForLocalAppsUnchecked(profile);
+  base::test::TestFuture<base::WeakPtr<Browser>,
+                         base::WeakPtr<content::WebContents>,
+                         apps::LaunchContainer>
+      future;
+  provider->scheduler().LaunchAppWithCustomParams(std::move(params),
+                                                  future.GetCallback());
+  return future.template Get<1>().get();
 }
 
 content::EvalJsResult ReadTextContent(content::WebContents* web_contents,
diff --git a/chrome/browser/ui/webui/accessibility/accessibility_ui.cc b/chrome/browser/ui/webui/accessibility/accessibility_ui.cc
index 8f425bd..049ae38 100644
--- a/chrome/browser/ui/webui/accessibility/accessibility_ui.cc
+++ b/chrome/browser/ui/webui/accessibility/accessibility_ui.cc
@@ -56,12 +56,12 @@
 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
 #include "ui/accessibility/platform/inspect/ax_tree_formatter.h"
 #include "ui/base/webui/web_ui_util.h"
-#include "ui/views/accessibility/view_accessibility.h"
 
 #if !BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 #endif
diff --git a/chrome/browser/ui/webui/ash/login/BUILD.gn b/chrome/browser/ui/webui/ash/login/BUILD.gn
index a5eb1e6e..0ce5e36 100644
--- a/chrome/browser/ui/webui/ash/login/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/login/BUILD.gn
@@ -271,6 +271,7 @@
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/profiles:signin_profile_handler",
     "//chrome/browser/ash/system",
+    "//chrome/browser/certificate_provider",
     "//chrome/browser/extensions",
     "//chrome/browser/policy:onc",
     "//chrome/browser/profiles:profile",
diff --git a/chrome/browser/ui/webui/ash/settings/pages/people/os_sync_handler.cc b/chrome/browser/ui/webui/ash/settings/pages/people/os_sync_handler.cc
index f15f0c4..35d8c89 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/people/os_sync_handler.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/people/os_sync_handler.cc
@@ -75,6 +75,12 @@
   PushSyncPrefs();
 }
 
+void OSSyncHandler::OnSyncShutdown(syncer::SyncService* service) {
+  // Unreachable, since this class is tied to UI which gets destroyed before the
+  // Profile and its KeyedServices.
+  NOTREACHED();
+}
+
 void OSSyncHandler::HandleDidNavigateToOsSyncPage(
     const base::Value::List& args) {
   HandleOsSyncPrefsDispatch(args);
diff --git a/chrome/browser/ui/webui/ash/settings/pages/people/os_sync_handler.h b/chrome/browser/ui/webui/ash/settings/pages/people/os_sync_handler.h
index cc34b4e..23c2b6ea 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/people/os_sync_handler.h
+++ b/chrome/browser/ui/webui/ash/settings/pages/people/os_sync_handler.h
@@ -37,6 +37,7 @@
 
   // syncer::SyncServiceObserver:
   void OnStateChanged(syncer::SyncService* service) override;
+  void OnSyncShutdown(syncer::SyncService* service) override;
 
   // Callbacks from the page. Visible for testing.
   void HandleDidNavigateToOsSyncPage(const base::Value::List& args);
diff --git a/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.cc b/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.cc
index 3a7bd6e..8fd22ef8 100644
--- a/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.cc
+++ b/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.cc
@@ -448,6 +448,13 @@
   }
 }
 
+void BookmarksMessageHandler::OnSyncShutdown(
+    syncer::SyncService* sync_service) {
+  // Unreachable, since this class is tied to UI which gets destroyed before the
+  // Profile and its KeyedServices.
+  NOTREACHED();
+}
+
 void BookmarksMessageHandler::ExtensiveBookmarkChangesBeginning() {
   batch_updates_ongoing_ = true;
 }
diff --git a/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.h b/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.h
index 2593fb4..0384e50d 100644
--- a/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.h
+++ b/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.h
@@ -64,6 +64,7 @@
 
   // syncer::SyncServiceObserver:
   void OnStateChanged(syncer::SyncService* sync_service) override;
+  void OnSyncShutdown(syncer::SyncService* sync_service) override;
 
   // bookmarks::BookmarkModelObserver
   void BookmarkModelLoaded(bool ids_reassigned) override;
diff --git a/chrome/browser/ui/webui/customize_buttons/BUILD.gn b/chrome/browser/ui/webui/customize_buttons/BUILD.gn
index 23993ae..6e5cfb9 100644
--- a/chrome/browser/ui/webui/customize_buttons/BUILD.gn
+++ b/chrome/browser/ui/webui/customize_buttons/BUILD.gn
@@ -30,7 +30,10 @@
   sources = [ "customize_buttons.mojom" ]
   webui_module_path = "/"
 
-  public_deps = [ "//url/mojom:url_mojom_gurl" ]
+  public_deps = [
+    "//chrome/browser/ui/webui/side_panel/customize_chrome:mojo_bindings",
+    "//url/mojom:url_mojom_gurl",
+  ]
 }
 
 source_set("test_support") {
diff --git a/chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom b/chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom
index 426e7a0..42d244a 100644
--- a/chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom
+++ b/chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom
@@ -4,16 +4,7 @@
 
 module customize_buttons.mojom;
 
-// TODO(crbug.com/403572608) Dedupe CustomizeChromeSection mojom enums.
-enum CustomizeChromeSection {
-  kUnspecified,
-  kAppearance,
-  kShortcuts,
-  kModules,
-  kWallpaperSearch,
-  kToolbar,
-  kFooter,
-};
+import "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom";
 
 enum SidePanelOpenTrigger {
   kNewTabPage,
@@ -41,8 +32,8 @@
 
   // Shows or hides the customize chrome in unified side panel.
   SetCustomizeChromeSidePanelVisible(bool visible,
-                                     CustomizeChromeSection section,
-                                     SidePanelOpenTrigger trigger);
+      side_panel.mojom.CustomizeChromeSection section,
+      SidePanelOpenTrigger trigger);
 };
 
 // WebUI-side handler for requests from the browser.
diff --git a/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.cc b/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.cc
index 6658866..e668a7e4 100644
--- a/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.cc
+++ b/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.cc
@@ -103,7 +103,7 @@
 
 void CustomizeButtonsHandler::SetCustomizeChromeSidePanelVisible(
     bool visible,
-    customize_buttons::mojom::CustomizeChromeSection section,
+    CustomizeChromeSection section,
     customize_buttons::mojom::SidePanelOpenTrigger trigger) {
   customize_chrome::SidePanelController*
       customize_chrome_side_panel_controller =
@@ -116,32 +116,6 @@
     return;
   }
 
-  CustomizeChromeSection section_enum;
-  // TODO(crbug.com/419081665) Dedupe CustomizeChromeSection mojom enums.
-  switch (section) {
-    case customize_buttons::mojom::CustomizeChromeSection::kUnspecified:
-      section_enum = CustomizeChromeSection::kUnspecified;
-      break;
-    case customize_buttons::mojom::CustomizeChromeSection::kAppearance:
-      section_enum = CustomizeChromeSection::kAppearance;
-      break;
-    case customize_buttons::mojom::CustomizeChromeSection::kShortcuts:
-      section_enum = CustomizeChromeSection::kShortcuts;
-      break;
-    case customize_buttons::mojom::CustomizeChromeSection::kModules:
-      section_enum = CustomizeChromeSection::kModules;
-      break;
-    case customize_buttons::mojom::CustomizeChromeSection::kWallpaperSearch:
-      section_enum = CustomizeChromeSection::kWallpaperSearch;
-      break;
-    case customize_buttons::mojom::CustomizeChromeSection::kToolbar:
-      section_enum = CustomizeChromeSection::kToolbar;
-      break;
-    case customize_buttons::mojom::CustomizeChromeSection::kFooter:
-      section_enum = CustomizeChromeSection::kFooter;
-      break;
-  }
-
   SidePanelOpenTrigger trigger_enum;
   switch (trigger) {
     case customize_buttons::mojom::SidePanelOpenTrigger::kNewTabPage:
@@ -153,8 +127,7 @@
   }
 
   NotifyCustomizeChromeSidePanelVisibilityChanged(true);
-  customize_chrome_side_panel_controller->OpenSidePanel(trigger_enum,
-                                                        section_enum);
+  customize_chrome_side_panel_controller->OpenSidePanel(trigger_enum, section);
 
   // Record usage for customize chrome promo.
   auto* tab = GetActiveTab();
diff --git a/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.h b/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.h
index e726d3ba..c8c7ab9 100644
--- a/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.h
+++ b/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.h
@@ -9,6 +9,7 @@
 #include "chrome/browser/new_tab_page/feature_promo_helper/new_tab_page_feature_promo_helper.h"
 #include "chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.h"
 #include "chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom.h"
+#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_section.h"
 #include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -45,7 +46,7 @@
   // Wallpaper Search button.
   void SetCustomizeChromeSidePanelVisible(
       bool visible,
-      customize_buttons::mojom::CustomizeChromeSection section,
+      CustomizeChromeSection section,
       customize_buttons::mojom::SidePanelOpenTrigger triger) override;
 
  private:
diff --git a/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler_browsertest.cc b/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler_browsertest.cc
index 98db18dc..26fa59c 100644
--- a/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler_browsertest.cc
@@ -96,7 +96,7 @@
             std::move(mock_controller_ptr));
   }
 
-  void CreateHandlerWithTabInterface(bool set_tab_interface) {
+  void CreateHandler(bool set_tab_interface) {
     tabs::TabInterface* tab = nullptr;
     if (set_tab_interface) {
       tab = browser()->tab_strip_model()->GetActiveTab();
@@ -138,7 +138,7 @@
  public:
   void SetUpOnMainThread() override {
     CustomizeButtonsHandlerBrowserTestBase::SetUpOnMainThread();
-    CreateHandlerWithTabInterface(/*set_tab_interface=*/GetParam());
+    CreateHandler(/*set_tab_interface=*/GetParam());
   }
 };
 
@@ -161,7 +161,7 @@
                                      testing::SaveArg<1>(&section)));
   EXPECT_CALL(doc_, SetCustomizeChromeSidePanelVisibility)
       .Times(2)
-      .WillRepeatedly([&visible](bool visible_arg) { visible = visible_arg; });
+      .WillRepeatedly(testing::SaveArg<0>(&visible));
   EXPECT_CALL(
       *GetMockFeaturePromoHelper(),
       RecordPromoFeatureUsageAndClosePromo(
@@ -177,8 +177,7 @@
       .Times(2);
 
   handler_->SetCustomizeChromeSidePanelVisible(
-      /*visible=*/true,
-      customize_buttons::mojom::CustomizeChromeSection::kUnspecified,
+      /*visible=*/true, CustomizeChromeSection::kUnspecified,
       customize_buttons::mojom::SidePanelOpenTrigger::kNewTabFooter);
   doc_.FlushForTesting();
 
@@ -187,8 +186,7 @@
   EXPECT_TRUE(visible);
 
   handler_->SetCustomizeChromeSidePanelVisible(
-      /*visible=*/true,
-      customize_buttons::mojom::CustomizeChromeSection::kAppearance,
+      /*visible=*/true, CustomizeChromeSection::kAppearance,
       customize_buttons::mojom::SidePanelOpenTrigger::kNewTabPage);
   doc_.FlushForTesting();
 
@@ -209,8 +207,7 @@
       .Times(0);
 
   handler_->SetCustomizeChromeSidePanelVisible(
-      /*visible=*/false,
-      customize_buttons::mojom::CustomizeChromeSection::kModules,
+      /*visible=*/false, CustomizeChromeSection::kModules,
       customize_buttons::mojom::SidePanelOpenTrigger::kNewTabPage);
   doc_.FlushForTesting();
 }
@@ -246,44 +243,19 @@
             1);
 }
 
-class CustomizeButtonsHandlerVisibilityParamsTest
+class CustomizeButtonsHandlerTriggerParamTest
     : public CustomizeButtonsHandlerBrowserTestBase,
       public testing::WithParamInterface<
-          std::tuple<customize_buttons::mojom::CustomizeChromeSection,
-                     customize_buttons::mojom::SidePanelOpenTrigger>> {
+          customize_buttons::mojom::SidePanelOpenTrigger> {
  public:
   void SetUpOnMainThread() override {
     CustomizeButtonsHandlerBrowserTestBase::SetUpOnMainThread();
-    CreateHandlerWithTabInterface(/*set_tab_interface=*/false);
+    CreateHandler(/*set_tab_interface=*/false);
   }
 
  protected:
-  customize_buttons::mojom::CustomizeChromeSection section_param() const {
-    return std::get<0>(GetParam());
-  }
   customize_buttons::mojom::SidePanelOpenTrigger trigger_param() const {
-    return std::get<1>(GetParam());
-  }
-
-  CustomizeChromeSection GetExpectedSection(
-      customize_buttons::mojom::CustomizeChromeSection section) {
-    // TODO(crbug.com/419081665) Dedupe CustomizeChromeSection mojom enums.
-    switch (section) {
-      case customize_buttons::mojom::CustomizeChromeSection::kUnspecified:
-        return CustomizeChromeSection::kUnspecified;
-      case customize_buttons::mojom::CustomizeChromeSection::kAppearance:
-        return CustomizeChromeSection::kAppearance;
-      case customize_buttons::mojom::CustomizeChromeSection::kShortcuts:
-        return CustomizeChromeSection::kShortcuts;
-      case customize_buttons::mojom::CustomizeChromeSection::kModules:
-        return CustomizeChromeSection::kModules;
-      case customize_buttons::mojom::CustomizeChromeSection::kWallpaperSearch:
-        return CustomizeChromeSection::kWallpaperSearch;
-      case customize_buttons::mojom::CustomizeChromeSection::kToolbar:
-        return CustomizeChromeSection::kToolbar;
-      case customize_buttons::mojom::CustomizeChromeSection::kFooter:
-        return CustomizeChromeSection::kFooter;
-    }
+    return GetParam();
   }
 
   SidePanelOpenTrigger GetExpectedTrigger(
@@ -299,21 +271,12 @@
 
 INSTANTIATE_TEST_SUITE_P(
     All,
-    CustomizeButtonsHandlerVisibilityParamsTest,
-    testing::Combine(
-        testing::Values(
-            customize_buttons::mojom::CustomizeChromeSection::kUnspecified,
-            customize_buttons::mojom::CustomizeChromeSection::kAppearance,
-            customize_buttons::mojom::CustomizeChromeSection::kShortcuts,
-            customize_buttons::mojom::CustomizeChromeSection::kModules,
-            customize_buttons::mojom::CustomizeChromeSection::kWallpaperSearch,
-            customize_buttons::mojom::CustomizeChromeSection::kToolbar),
-        testing::Values(
-            customize_buttons::mojom::SidePanelOpenTrigger::kNewTabPage,
-            customize_buttons::mojom::SidePanelOpenTrigger::kNewTabFooter)));
+    CustomizeButtonsHandlerTriggerParamTest,
+    testing::Values(
+        customize_buttons::mojom::SidePanelOpenTrigger::kNewTabPage,
+        customize_buttons::mojom::SidePanelOpenTrigger::kNewTabFooter));
 
-IN_PROC_BROWSER_TEST_P(CustomizeButtonsHandlerVisibilityParamsTest,
-                       OpenSidePanel) {
+IN_PROC_BROWSER_TEST_P(CustomizeButtonsHandlerTriggerParamTest, OpenSidePanel) {
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   std::optional<CustomizeChromeSection> section;
@@ -338,8 +301,7 @@
       .Times(1);
 
   handler_->SetCustomizeChromeSidePanelVisible(
-      /*visible=*/true, section_param(), trigger_param());
+      /*visible=*/true, CustomizeChromeSection::kUnspecified, trigger_param());
 
-  EXPECT_EQ(section.value(), GetExpectedSection(section_param()));
   EXPECT_EQ(trigger, GetExpectedTrigger(trigger_param()));
 }
diff --git a/chrome/browser/ui/webui/history/history_ui.cc b/chrome/browser/ui/webui/history/history_ui.cc
index 24d39b8..f13c85b4 100644
--- a/chrome/browser/ui/webui/history/history_ui.cc
+++ b/chrome/browser/ui/webui/history/history_ui.cc
@@ -47,7 +47,6 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/history_resources.h"
 #include "chrome/grit/history_resources_map.h"
-#include "chrome/grit/locale_settings.h"
 #include "components/grit/components_scaled_resources.h"
 #include "components/history/core/common/pref_names.h"
 #include "components/history_clusters/core/config.h"
@@ -118,24 +117,19 @@
   // the user's settings.
   source->AddString(
       "sidebarFooterGMAOnly",
-      l10n_util::GetStringFUTF16(
-          IDS_HISTORY_OTHER_FORMS_OF_HISTORY_GMA_ONLY,
-          l10n_util::GetStringUTF16(
-              IDS_SETTINGS_CLEAR_DATA_MYACTIVITY_URL_IN_HISTORY)));
+      l10n_util::GetStringFUTF16(IDS_HISTORY_OTHER_FORMS_OF_HISTORY_GMA_ONLY,
+                                 chrome::kMyActivityUrlInHistory));
   source->AddString(
       "sidebarFooterGAAOnly",
       l10n_util::GetStringFUTF16(IDS_HISTORY_OTHER_FORMS_OF_HISTORY_GAA_ONLY,
                                  chrome::kMyActivityGeminiAppsUrl));
-  source->AddString("sidebarFooterGMAAndGAA",
-                    l10n_util::GetStringFUTF16(
-                        IDS_HISTORY_OTHER_FORMS_OF_HISTORY_GMA_AND_GAA,
-                        l10n_util::GetStringUTF16(
-                            IDS_SETTINGS_CLEAR_DATA_MYACTIVITY_URL_IN_HISTORY),
-                        chrome::kMyActivityGeminiAppsUrl));
+  source->AddString(
+      "sidebarFooterGMAAndGAA",
+      l10n_util::GetStringFUTF16(IDS_HISTORY_OTHER_FORMS_OF_HISTORY_GMA_AND_GAA,
+                                 chrome::kMyActivityUrlInHistory,
+                                 chrome::kMyActivityGeminiAppsUrl));
   // Links that are used in the messages above.
-  source->AddString("sidebarFooterGMALink",
-                    l10n_util::GetStringUTF16(
-                        IDS_SETTINGS_CLEAR_DATA_MYACTIVITY_URL_IN_HISTORY));
+  source->AddString("sidebarFooterGMALink", chrome::kMyActivityUrlInHistory);
   source->AddString("sidebarFooterGAALink", chrome::kMyActivityGeminiAppsUrl);
 
 #if BUILDFLAG(ENABLE_GLIC)
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
index f26bd6f..5d166f0 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
@@ -51,13 +51,10 @@
   // page title and body content that is expected.
   //
   // page_title must be an exact match, while body content may appear anywhere
-  // in the rendered page. Thus an empty body_text never fails. If
-  // expand_details is set, body_text will also match any content hidden below
-  // the fold.
+  // in the rendered page. Thus an empty body_text never fails.
   void TestInterstitial(GURL url,
                         const std::string& page_title,
-                        const std::u16string& body_text,
-                        bool expand_details) {
+                        const std::u16string& body_text) {
     ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
     EXPECT_EQ(base::ASCIIToUTF16(page_title),
               browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
@@ -75,13 +72,6 @@
     content::WebContents* contents =
         browser()->tab_strip_model()->GetActiveWebContents();
 
-    if (expand_details) {
-      EXPECT_EQ(true,
-                content::EvalJs(
-                    contents,
-                    "document.querySelector('#details-button').click(); true"));
-    }
-
     EXPECT_GE(ui_test_utils::FindInPage(contents, body_text, true, true,
                                         nullptr, nullptr),
               1);
@@ -89,8 +79,7 @@
 
   // Convenience function to test interstitial pages without provided body_text.
   void TestInterstitial(GURL url, const std::string& page_title) {
-    TestInterstitial(url, page_title, std::u16string(),
-                     /*expand_details=*/false);
+    TestInterstitial(url, page_title, std::u16string());
   }
 
   // Convenience function to test interstitial pages with l10n message_ids as
@@ -98,23 +87,7 @@
   void TestInterstitial(GURL url,
                         const std::string& page_title,
                         int message_id) {
-    TestInterstitial(url, page_title, l10n_util::GetStringUTF16(message_id),
-                     /*expand_details=*/false);
-  }
-
-  // Default version of TestInterstitial that uses expand_details=false.
-  void TestInterstitial(GURL url,
-                        const std::string& page_title,
-                        const std::u16string& body_text) {
-    TestInterstitial(url, page_title, body_text, /*expand_details=*/false);
-  }
-
-  // Convenience function to test interstitial pages where the body_text is
-  // below the fold.
-  void TestInterstitialExpandedDetails(GURL url,
-                                       const std::string& page_title,
-                                       const std::u16string& body_text) {
-    TestInterstitial(url, page_title, body_text, /*expand_details=*/true);
+    TestInterstitial(url, page_title, l10n_util::GetStringUTF16(message_id));
   }
 };
 
@@ -146,15 +119,15 @@
 }
 
 IN_PROC_BROWSER_TEST_F(InterstitialUITest, PinnedCertInterstitial) {
-  TestInterstitialExpandedDetails(
-      GURL("chrome://interstitials/ssl?type=hpkp_failure"), "Privacy error",
-      u"NET::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN");
+  TestInterstitial(GURL("chrome://interstitials/ssl?type=hpkp_failure"),
+                   "Privacy error",
+                   u"NET::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN");
 }
 
 IN_PROC_BROWSER_TEST_F(InterstitialUITest, CTInterstitial) {
-  TestInterstitialExpandedDetails(
-      GURL("chrome://interstitials/ssl?type=ct_failure"), "Privacy error",
-      u"NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED");
+  TestInterstitial(GURL("chrome://interstitials/ssl?type=ct_failure"),
+                   "Privacy error",
+                   u"NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED");
 }
 
 IN_PROC_BROWSER_TEST_F(InterstitialUITest, EnterpriseBlockInterstitial) {
diff --git a/chrome/browser/ui/webui/password_manager/sync_handler.cc b/chrome/browser/ui/webui/password_manager/sync_handler.cc
index 10fc63c..c91d6de 100644
--- a/chrome/browser/ui/webui/password_manager/sync_handler.cc
+++ b/chrome/browser/ui/webui/password_manager/sync_handler.cc
@@ -230,6 +230,12 @@
   }
 }
 
+void SyncHandler::OnSyncShutdown(syncer::SyncService* sync_service) {
+  // Unreachable, since this class is tied to UI which gets destroyed before the
+  // Profile and its KeyedServices.
+  NOTREACHED();
+}
+
 void SyncHandler::FireOnGetLocalDataDescriptionReceived(
     std::map<syncer::DataType, syncer::LocalDataDescription> data) {
   int local_password_count =
diff --git a/chrome/browser/ui/webui/password_manager/sync_handler.h b/chrome/browser/ui/webui/password_manager/sync_handler.h
index 3d4d7ec3..aea49920 100644
--- a/chrome/browser/ui/webui/password_manager/sync_handler.h
+++ b/chrome/browser/ui/webui/password_manager/sync_handler.h
@@ -77,6 +77,7 @@
 
   // syncer::SyncServiceObserver implementation.
   void OnStateChanged(syncer::SyncService* sync_service) override;
+  void OnSyncShutdown(syncer::SyncService* sync_service) override;
 
   // IdentityManager::Observer implementation.
   void OnExtendedAccountInfoUpdated(const AccountInfo& info) override;
diff --git a/chrome/browser/ui/webui/searchbox/searchbox_handler.cc b/chrome/browser/ui/webui/searchbox/searchbox_handler.cc
index 05cf91b..5adc05c 100644
--- a/chrome/browser/ui/webui/searchbox/searchbox_handler.cc
+++ b/chrome/browser/ui/webui/searchbox/searchbox_handler.cc
@@ -109,6 +109,8 @@
     "//resources/cr_components/searchbox/icons/drive_slides.svg";
 constexpr char kDriveVideoIconResourceName[] =
     "//resources/cr_components/searchbox/icons/drive_video.svg";
+constexpr char kEnterpriseIconResourceName[] =
+    "//resources/cr_components/searchbox/icons/enterprise.svg";
 constexpr char kExtensionAppIconResourceName[] =
     "//resources/cr_components/searchbox/icons/extension_app.svg";
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
@@ -320,7 +322,7 @@
   source->AddBoolean("enableCsbMotionTweaks", false);
 
   static constexpr webui::LocalizedString kStrings[] = {
-      {"lensSearchButtonLabel", IDS_TOOLTIP_LENS_SEARCH},
+      {"lensSearchButtonLabel", IDS_TOOLTIP_LENS_REINVOKE_VISUAL_SELECTION_A11Y_LABEL},
       {"searchboxSeparator", IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR},
       {"removeSuggestion", IDS_OMNIBOX_REMOVE_SUGGESTION},
       {"searchBoxHint", IDS_GOOGLE_SEARCH_BOX_EMPTY_HINT_MD},
@@ -412,6 +414,8 @@
                         ? ntp_realbox::RealboxLayoutModeToString(
                               ntp_realbox::kRealboxLayoutMode.Get())
                         : "");
+  source->AddBoolean("ntpRealboxNextEnabled",
+                     ntp_realbox::IsNtpRealboxNextEnabled(profile));
   source->AddBoolean("searchboxCyclingPlaceholders",
                      ntp_realbox::IsNtpRealboxNextEnabled(profile) &&
                          ntp_realbox::kCyclingPlaceholders.Get());
@@ -465,9 +469,7 @@
   } else if (icon.name == omnibox::kDriveVideoIcon.name) {
     return kDriveVideoIconResourceName;
   } else if (icon.name == omnibox::kEnterpriseIcon.name) {
-    // TODO(crbug.com/446953332): Add icon. Not necessary ATM because IPH
-    //   matches aren't shown in webUI.
-    NOTREACHED();
+    return kEnterpriseIconResourceName;
   } else if (icon.name == omnibox::kExtensionAppIcon.name) {
     return kExtensionAppIconResourceName;
   } else if (icon.name == omnibox::kIncognitoCr2023Icon.name) {
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index f3dca16..79a4bc3 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -1123,6 +1123,12 @@
   PushTrustedVaultBannerState();
 }
 
+void PeopleHandler::OnSyncShutdown(syncer::SyncService* sync_service) {
+  // Unreachable, since this class is tied to UI which gets destroyed before the
+  // Profile and its KeyedServices.
+  NOTREACHED();
+}
+
 void PeopleHandler::BeforeUnloadDialogCancelled() {
   // The before unload dialog is only shown during the first sync setup.
   DCHECK(IdentityManagerFactory::GetForProfile(profile_)->HasPrimaryAccount(
diff --git a/chrome/browser/ui/webui/settings/people_handler.h b/chrome/browser/ui/webui/settings/people_handler.h
index 3a2ac883..27c1b18 100644
--- a/chrome/browser/ui/webui/settings/people_handler.h
+++ b/chrome/browser/ui/webui/settings/people_handler.h
@@ -179,6 +179,7 @@
 
   // syncer::SyncServiceObserver implementation.
   void OnStateChanged(syncer::SyncService* sync_service) override;
+  void OnSyncShutdown(syncer::SyncService* sync_service) override;
 
   // content::WebContentsObserver implementation
   void BeforeUnloadDialogCancelled() override;
diff --git a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
index f4242ed2..4add83f 100644
--- a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
@@ -373,6 +373,12 @@
   UpdateSyncState();
 }
 
+void ClearBrowsingDataHandler::OnSyncShutdown(syncer::SyncService* sync) {
+  // Unreachable, since this class is tied to UI which gets destroyed before the
+  // Profile and its KeyedServices.
+  NOTREACHED();
+}
+
 void ClearBrowsingDataHandler::UpdateSyncState() {
   FireWebUIListener("update-sync-state", CreateSyncStateEvent());
 }
diff --git a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h
index 91c72b1..06da3c6 100644
--- a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h
+++ b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h
@@ -89,6 +89,7 @@
 
   // Implementation of SyncServiceObserver.
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
   // Updates the footer of the dialog when the sync state changes.
   virtual void UpdateSyncState();
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 05cdc25c..4a6d5b5 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -53,7 +53,6 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
-#include "chrome/grit/locale_settings.h"
 #include "components/autofill/content/browser/content_autofill_client.h"
 #include "components/autofill/content/browser/content_autofill_driver.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
@@ -674,12 +673,10 @@
       l10n_util::GetStringFUTF16(
           IDS_SETTINGS_CLEAR_GOOGLE_SEARCH_HISTORY_NON_GOOGLE_DSE,
           chrome::kMyActivityUrlInClearBrowsingData));
-  html_source->AddString(
-      "historyDeletionDialogBody",
-      l10n_util::GetStringFUTF16(
-          IDS_CLEAR_BROWSING_DATA_HISTORY_NOTICE,
-          l10n_util::GetStringUTF16(
-              IDS_SETTINGS_CLEAR_DATA_MYACTIVITY_URL_IN_DIALOG)));
+  html_source->AddString("historyDeletionDialogBody",
+                         l10n_util::GetStringFUTF16(
+                             IDS_CLEAR_BROWSING_DATA_HISTORY_NOTICE,
+                             chrome::kMyActivityUrlInClearBrowsingDataNotice));
   html_source->AddString(
       "passwordsDeletionDialogBody",
       l10n_util::GetStringFUTF16(
@@ -1236,7 +1233,7 @@
       {"yourSavedInfoPageDescription",
        IDS_SETTINGS_YOUR_SAVED_INFO_DESCRIPTION},
       {"yourSavedInfoRelatedServicesTitle",
-        IDS_SETTINGS_RELATED_SERVICES_TITLE},
+       IDS_SETTINGS_RELATED_SERVICES_TITLE},
       {"passwordsDescription", IDS_SETTINGS_PASSWORD_MANAGER_DESCRIPTION},
       {"genericCreditCard", IDS_AUTOFILL_CC_GENERIC},
       {"creditCards", IDS_AUTOFILL_PAYMENT_METHODS},
@@ -1474,7 +1471,9 @@
       {"removeAddressFromChrome",
        IDS_SETTINGS_HOME_AND_WORK_ADDRESS_REMOVE_CONFIRMATION_DIALOG_ACTION_OK},
       {"homeAndWorkAddressRemovedMessage",
-       IDS_SETTINGS_HOME_AND_WORK_ADDRESS_REMOVED_MESSAGE}};
+       IDS_SETTINGS_HOME_AND_WORK_ADDRESS_REMOVED_MESSAGE},
+      {"nameEmailAddressRemovedMessage",
+       IDS_SETTINGS_NAME_EMAIL_ADDRESS_REMOVED_MESSAGE}};
 
   html_source->AddString("manageAddressesUrl",
                          autofill::payments::GetManageAddressesUrl().spec());
@@ -2002,7 +2001,7 @@
        IDS_SETTINGS_ENABLE_DO_NOT_TRACK_DIALOG_LEARN_MORE_ACCESSIBILITY_LABEL},
       // TODO(crbug.com/40122957): This string is no longer used. Remove.
       {"permissionsPageTitle", IDS_SETTINGS_PERMISSIONS},
-      {"permissionsPageDescription", IDS_SETTINGS_PERMISSIONS_DESCRIPTION},
+      {"siteSettingsSublabel", IDS_SETTINGS_PERMISSIONS_DESCRIPTION},
       {"securityPageTitle", IDS_SETTINGS_SECURITY},
       {"securityPageDescription", IDS_SETTINGS_SECURITY_DESCRIPTION},
       {"advancedProtectionProgramTitle",
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index 5a75b16b..75cbac6 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -91,6 +91,7 @@
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "components/privacy_sandbox/privacy_sandbox_features.h"
+#include "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/app_update.h"
@@ -588,6 +589,24 @@
   return exception;
 }
 
+void MaybeLogSafeBrowsingNotificationRevocationSource(
+    ContentSettingsType permission_type,
+    ContentSetting previous_setting_value,
+    ContentSetting new_setting_value) {
+  // If notification permission changes from allowed to not allowed, log the
+  // histogram.
+  if (permission_type == ContentSettingsType::NOTIFICATIONS &&
+      previous_setting_value == CONTENT_SETTING_ALLOW &&
+      (new_setting_value == CONTENT_SETTING_BLOCK ||
+       new_setting_value == CONTENT_SETTING_DEFAULT ||
+       new_setting_value == CONTENT_SETTING_ASK)) {
+    safe_browsing::SafeBrowsingMetricsCollector::
+        LogSafeBrowsingNotificationRevocationSourceHistogram(
+            safe_browsing::NotificationRevocationSource::
+                kUserManuallyChangedSiteSetting);
+  }
+}
+
 }  // namespace
 
 // static
@@ -1671,6 +1690,12 @@
       constraints.set_track_last_visit_for_autoexpiration(true);
     }
 
+    MaybeLogSafeBrowsingNotificationRevocationSource(
+        content_type, /*previous_setting_value=*/
+        map->GetContentSetting(origin, origin,
+                               ContentSettingsType::NOTIFICATIONS),
+        /*new_setting_value=*/setting);
+
     map->SetContentSettingDefaultScope(origin, origin, content_type, setting,
                                        constraints);
 
@@ -1787,6 +1812,15 @@
   HostContentSettingsMap* map =
       HostContentSettingsMapFactory::GetForProfile(profile);
 
+  GURL origin(primary_pattern_string);
+  if (origin.is_valid()) {
+    MaybeLogSafeBrowsingNotificationRevocationSource(
+        content_type, /*previous_setting_value=*/
+        map->GetContentSetting(origin, origin,
+                               ContentSettingsType::NOTIFICATIONS),
+        CONTENT_SETTING_DEFAULT);
+  }
+
   ContentSettingsPattern primary_pattern =
       ContentSettingsPattern::FromString(primary_pattern_string);
   ContentSettingsPattern secondary_pattern =
@@ -1875,6 +1909,16 @@
           ? ContentSettingsPattern::Wildcard()
           : ContentSettingsPattern::FromString(secondary_pattern_string);
 
+  GURL primary_url(primary_pattern.ToString());
+  if (primary_url.is_valid()) {
+    MaybeLogSafeBrowsingNotificationRevocationSource(
+        content_type,
+        /*previous_setting_value=*/
+        map->GetContentSetting(primary_url, primary_url,
+                               ContentSettingsType::NOTIFICATIONS),
+        /*new_setting_value=*/setting);
+  }
+
   // Clear any existing embargo status if the new setting isn't block.
   if (setting != CONTENT_SETTING_BLOCK) {
     GURL url(primary_pattern.ToString());
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index 20415ae6..d2385af 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -107,6 +107,7 @@
 #include "components/permissions/test/permission_test_util.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/privacy_sandbox/privacy_sandbox_features.h"
+#include "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/app_update.h"
@@ -6977,4 +6978,173 @@
   EXPECT_EQ(base::Time(), info.metadata.last_visited());
 }
 
+using SiteSettingsHandlerNotificationRevocationHistogramTest =
+    SiteSettingsHandlerTest;
+
+TEST_F(SiteSettingsHandlerNotificationRevocationHistogramTest,
+       HandleSetOriginPermissionsAllowToBlock) {
+  base::HistogramTester histograms;
+  const GURL primary_url("https://example.com");
+  base::Value::List allow_args;
+  allow_args.Append(primary_url.spec());
+  allow_args.Append(std::move(kNotifications));
+  allow_args.Append(
+      content_settings::ContentSettingToString(CONTENT_SETTING_ALLOW));
+  handler()->HandleSetOriginPermissions(allow_args);
+
+  // Update NOTIFICATIONS to BLOCK.
+  base::Value::List block_args;
+  block_args.Append(primary_url.spec());
+  block_args.Append(std::move(kNotifications));
+  block_args.Append(
+      content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+  handler()->HandleSetOriginPermissions(block_args);
+
+  // Verify histogram is logged as expected.
+  histograms.ExpectUniqueSample("SafeBrowsing.NotificationRevocationSource",
+                                safe_browsing::NotificationRevocationSource::
+                                    kUserManuallyChangedSiteSetting,
+                                1);
+}
+
+TEST_F(SiteSettingsHandlerNotificationRevocationHistogramTest,
+       HandleSetOriginPermissionsAllowToAsk) {
+  base::HistogramTester histograms;
+  const GURL primary_url("https://example.com");
+  base::Value::List allow_args;
+  allow_args.Append(primary_url.spec());
+  allow_args.Append(std::move(kNotifications));
+  allow_args.Append(
+      content_settings::ContentSettingToString(CONTENT_SETTING_ALLOW));
+  handler()->HandleSetOriginPermissions(allow_args);
+
+  // Update NOTIFICATIONS to ASK.
+  base::Value::List block_args;
+  block_args.Append(primary_url.spec());
+  block_args.Append(std::move(kNotifications));
+  block_args.Append(
+      content_settings::ContentSettingToString(CONTENT_SETTING_ASK));
+  handler()->HandleSetOriginPermissions(block_args);
+
+  // Verify histogram is logged as expected.
+  histograms.ExpectUniqueSample("SafeBrowsing.NotificationRevocationSource",
+                                safe_browsing::NotificationRevocationSource::
+                                    kUserManuallyChangedSiteSetting,
+                                1);
+}
+
+TEST_F(SiteSettingsHandlerNotificationRevocationHistogramTest,
+       HandleSetOriginPermissionsAllowToDefault) {
+  base::HistogramTester histograms;
+  const GURL primary_url("https://example.com");
+  base::Value::List allow_args;
+  allow_args.Append(primary_url.spec());
+  allow_args.Append(std::move(kNotifications));
+  allow_args.Append(
+      content_settings::ContentSettingToString(CONTENT_SETTING_ALLOW));
+  handler()->HandleSetOriginPermissions(allow_args);
+
+  // Update NOTIFICATIONS to DEFAULT.
+  base::Value::List block_args;
+  block_args.Append(primary_url.spec());
+  block_args.Append(std::move(kNotifications));
+  block_args.Append(
+      content_settings::ContentSettingToString(CONTENT_SETTING_DEFAULT));
+  handler()->HandleSetOriginPermissions(block_args);
+
+  // Verify histogram is logged as expected.
+  histograms.ExpectUniqueSample("SafeBrowsing.NotificationRevocationSource",
+                                safe_browsing::NotificationRevocationSource::
+                                    kUserManuallyChangedSiteSetting,
+                                1);
+}
+
+TEST_F(SiteSettingsHandlerNotificationRevocationHistogramTest,
+       HandleResetCategoryPermissionForPattern) {
+  base::HistogramTester histograms;
+
+  constexpr char kOrigin[] = "https://www.test.com:443";
+  base::Value::List set_args;
+  set_args.Append(kOrigin);        // Primary pattern.
+  set_args.Append(std::string());  // Secondary pattern.
+  set_args.Append(kNotifications);
+  set_args.Append(
+      content_settings::ContentSettingToString(CONTENT_SETTING_ALLOW));
+  set_args.Append(false);  // Incognito.
+  handler()->HandleSetCategoryPermissionForPattern(set_args);
+
+  base::Value::List reset_args;
+  reset_args.Append(kOrigin);        // Primary pattern.
+  reset_args.Append(std::string());  // Secondary pattern.
+  reset_args.Append(kNotifications);
+  reset_args.Append(false);  // Incognito
+  handler()->HandleResetCategoryPermissionForPattern(reset_args);
+
+  // Verify histogram is logged as expected.
+  histograms.ExpectUniqueSample("SafeBrowsing.NotificationRevocationSource",
+                                safe_browsing::NotificationRevocationSource::
+                                    kUserManuallyChangedSiteSetting,
+                                1);
+}
+
+TEST_F(SiteSettingsHandlerNotificationRevocationHistogramTest,
+       HandleSetCategoryPermissionForPatternAllowToAsk) {
+  base::HistogramTester histograms;
+
+  constexpr char kOrigin[] = "https://www.test.com:443";
+  base::Value::List set_args;
+  set_args.Append(kOrigin);        // Primary pattern.
+  set_args.Append(std::string());  // Secondary pattern.
+  set_args.Append(kNotifications);
+  set_args.Append(
+      content_settings::ContentSettingToString(CONTENT_SETTING_ALLOW));
+  set_args.Append(false);  // Incognito.
+  handler()->HandleSetCategoryPermissionForPattern(set_args);
+
+  base::Value::List reset_args;
+  reset_args.Append(kOrigin);        // Primary pattern.
+  reset_args.Append(std::string());  // Secondary pattern.
+  reset_args.Append(kNotifications);
+  reset_args.Append(
+      content_settings::ContentSettingToString(CONTENT_SETTING_ASK));
+  reset_args.Append(false);  // Incognito.
+  handler()->HandleSetCategoryPermissionForPattern(reset_args);
+
+  // Verify histogram is logged as expected.
+  histograms.ExpectUniqueSample("SafeBrowsing.NotificationRevocationSource",
+                                safe_browsing::NotificationRevocationSource::
+                                    kUserManuallyChangedSiteSetting,
+                                1);
+}
+
+TEST_F(SiteSettingsHandlerNotificationRevocationHistogramTest,
+       HandleSetCategoryPermissionForPatternAllowToBlock) {
+  base::HistogramTester histograms;
+
+  constexpr char kOrigin[] = "https://www.test.com:443";
+  base::Value::List set_args;
+  set_args.Append(kOrigin);        // Primary pattern.
+  set_args.Append(std::string());  // Secondary pattern.
+  set_args.Append(kNotifications);
+  set_args.Append(
+      content_settings::ContentSettingToString(CONTENT_SETTING_ALLOW));
+  set_args.Append(false);  // Incognito.
+  handler()->HandleSetCategoryPermissionForPattern(set_args);
+
+  base::Value::List reset_args;
+  reset_args.Append(kOrigin);        // Primary pattern.
+  reset_args.Append(std::string());  // Secondary pattern.
+  reset_args.Append(kNotifications);
+  reset_args.Append(
+      content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+  reset_args.Append(false);  // Incognito.
+  handler()->HandleSetCategoryPermissionForPattern(reset_args);
+
+  // Verify histogram is logged as expected.
+  histograms.ExpectUniqueSample("SafeBrowsing.NotificationRevocationSource",
+                                safe_browsing::NotificationRevocationSource::
+                                    kUserManuallyChangedSiteSetting,
+                                1);
+}
+
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/site_settings_helper.cc b/chrome/browser/ui/webui/settings/site_settings_helper.cc
index 11d7e63..7ce0deea5 100644
--- a/chrome/browser/ui/webui/settings/site_settings_helper.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_helper.cc
@@ -240,7 +240,6 @@
     {ContentSettingsType::SUB_APP_INSTALLATION_PROMPTS, nullptr},
     {ContentSettingsType::DIRECT_SOCKETS, nullptr},
     {ContentSettingsType::REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS, nullptr},
-    {ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL, nullptr},
     {ContentSettingsType::DISPLAY_MEDIA_SYSTEM_AUDIO, nullptr},
     {ContentSettingsType::STORAGE_ACCESS_HEADER_ORIGIN_TRIAL, nullptr},
     // TODO(crbug.com/368266658): Implement the UI for Direct Sockets PNA.
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/BUILD.gn b/chrome/browser/ui/webui/side_panel/customize_chrome/BUILD.gn
index 78d9a720..44e1f87 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/BUILD.gn
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/BUILD.gn
@@ -12,15 +12,40 @@
     "customize_toolbar/customize_toolbar.mojom",
     "wallpaper_search/wallpaper_search.mojom",
   ]
-  webui_module_path = "/"
   public_deps = [
     "//components/ntp_tiles:mojo_bindings",
     "//mojo/public/mojom/base",
     "//skia/public/mojom",
     "//url/mojom:url_mojom_gurl",
   ]
+
+  webui_module_path = "/"
+
+  cpp_typemaps = [
+    {
+      types = [
+        {
+          mojom = "side_panel.mojom.CustomizeChromeSection"
+          cpp = "::CustomizeChromeSection"
+        },
+      ]
+      traits_headers = [ "customize_chrome_mojom_traits.h" ]
+      traits_public_deps = [ ":mojom_traits" ]
+    },
+  ]
 }
 
 source_set("customize_chrome") {
   sources = [ "customize_chrome_section.h" ]
 }
+
+source_set("mojom_traits") {
+  sources = [ "customize_chrome_mojom_traits.h" ]
+
+  deps = [ ":customize_chrome" ]
+
+  public_deps = [
+    ":mojo_bindings_shared_cpp_sources",
+    "//base",
+  ]
+}
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom
index 71a72d95..5fabdec 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom
@@ -102,6 +102,7 @@
 };
 
 enum CustomizeChromeSection {
+  kUnspecified,
   kAppearance,
   kShortcuts,
   kModules,
@@ -249,7 +250,7 @@
   SetThemeEditable(bool is_theme_editable);
   // Scrolls side panel to |section|. Possibly a response to a call to
   // |CustomizeChromePageHandler.UpdateScrollToSection()|.
-  ScrollToSection(CustomizeChromeSection section);
+  ScrollToSection(side_panel.mojom.CustomizeChromeSection section);
   // Sets Information about the tab that is attached to the CustomizeChromePage.
   AttachedTabStateUpdated(NewTabPageType ntp_type);
   // Sets the name and description of the system that manages the new tab page,
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_mojom_traits.h b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_mojom_traits.h
new file mode 100644
index 0000000..56ca2d28
--- /dev/null
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_mojom_traits.h
@@ -0,0 +1,65 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_MOJOM_TRAITS_H_
+#define CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_MOJOM_TRAITS_H_
+
+#include "base/containers/fixed_flat_map.h"
+#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom-shared.h"
+#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_section.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<side_panel::mojom::CustomizeChromeSection,
+                  CustomizeChromeSection> {
+  static side_panel::mojom::CustomizeChromeSection ToMojom(
+      CustomizeChromeSection input) {
+    static constexpr auto section_map =
+        base::MakeFixedFlatMap<CustomizeChromeSection,
+                               side_panel::mojom::CustomizeChromeSection>(
+            {{CustomizeChromeSection::kUnspecified,
+              side_panel::mojom::CustomizeChromeSection::kUnspecified},
+             {CustomizeChromeSection::kAppearance,
+              side_panel::mojom::CustomizeChromeSection::kAppearance},
+             {CustomizeChromeSection::kShortcuts,
+              side_panel::mojom::CustomizeChromeSection::kShortcuts},
+             {CustomizeChromeSection::kModules,
+              side_panel::mojom::CustomizeChromeSection::kModules},
+             {CustomizeChromeSection::kWallpaperSearch,
+              side_panel::mojom::CustomizeChromeSection::kWallpaperSearch},
+             {CustomizeChromeSection::kToolbar,
+              side_panel::mojom::CustomizeChromeSection::kToolbar},
+             {CustomizeChromeSection::kFooter,
+              side_panel::mojom::CustomizeChromeSection::kFooter}});
+    return section_map.at(input);
+  }
+
+  static bool FromMojom(side_panel::mojom::CustomizeChromeSection input,
+                        CustomizeChromeSection* out) {
+    static constexpr auto section_map =
+        base::MakeFixedFlatMap<side_panel::mojom::CustomizeChromeSection,
+                               CustomizeChromeSection>(
+            {{side_panel::mojom::CustomizeChromeSection::kUnspecified,
+              CustomizeChromeSection::kUnspecified},
+             {side_panel::mojom::CustomizeChromeSection::kAppearance,
+              CustomizeChromeSection::kAppearance},
+             {side_panel::mojom::CustomizeChromeSection::kShortcuts,
+              CustomizeChromeSection::kShortcuts},
+             {side_panel::mojom::CustomizeChromeSection::kModules,
+              CustomizeChromeSection::kModules},
+             {side_panel::mojom::CustomizeChromeSection::kWallpaperSearch,
+              CustomizeChromeSection::kWallpaperSearch},
+             {side_panel::mojom::CustomizeChromeSection::kToolbar,
+              CustomizeChromeSection::kToolbar},
+             {side_panel::mojom::CustomizeChromeSection::kFooter,
+              CustomizeChromeSection::kFooter}});
+    *out = section_map.at(input);
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_MOJOM_TRAITS_H_
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
index 5f90d5f..22532e24 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
@@ -182,32 +182,11 @@
 void CustomizeChromePageHandler::ScrollToSection(
     CustomizeChromeSection section) {
   last_requested_section_ = section;
-  side_panel::mojom::CustomizeChromeSection mojo_section;
-  switch (section) {
-    case CustomizeChromeSection::kUnspecified:
-      // Cannot scroll to unspecified section.
-      return;
-    case CustomizeChromeSection::kAppearance:
-      mojo_section = side_panel::mojom::CustomizeChromeSection::kAppearance;
-      break;
-    case CustomizeChromeSection::kShortcuts:
-      mojo_section = side_panel::mojom::CustomizeChromeSection::kShortcuts;
-      break;
-    case CustomizeChromeSection::kModules:
-      mojo_section = side_panel::mojom::CustomizeChromeSection::kModules;
-      break;
-    case CustomizeChromeSection::kWallpaperSearch:
-      mojo_section =
-          side_panel::mojom::CustomizeChromeSection::kWallpaperSearch;
-      break;
-    case CustomizeChromeSection::kToolbar:
-      mojo_section = side_panel::mojom::CustomizeChromeSection::kToolbar;
-      break;
-    case CustomizeChromeSection::kFooter:
-      mojo_section = side_panel::mojom::CustomizeChromeSection::kFooter;
-      break;
+  if (section == CustomizeChromeSection::kUnspecified) {
+    // Cannot scroll to unspecified section.
+    return;
   }
-  page_->ScrollToSection(mojo_section);
+  page_->ScrollToSection(section);
 }
 
 void CustomizeChromePageHandler::AttachedTabStateUpdated(const GURL& url) {
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
index fdc7bb8..8b0ddda 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
@@ -179,9 +179,7 @@
                const std::vector<ntp_tiles::TileType>& disabled_shortcuts));
   MOCK_METHOD(void, SetTheme, (side_panel::mojom::ThemePtr));
   MOCK_METHOD(void, SetThemeEditable, (bool));
-  MOCK_METHOD(void,
-              ScrollToSection,
-              (side_panel::mojom::CustomizeChromeSection));
+  MOCK_METHOD(void, ScrollToSection, (CustomizeChromeSection));
   MOCK_METHOD(void,
               AttachedTabStateUpdated,
               (side_panel::mojom::NewTabPageType));
@@ -898,7 +896,7 @@
 }
 
 TEST_F(CustomizeChromePageHandlerTest, ScrollToSection) {
-  side_panel::mojom::CustomizeChromeSection section;
+  CustomizeChromeSection section;
   EXPECT_CALL(mock_page_, ScrollToSection)
       .Times(1)
       .WillOnce(SaveArg<0>(&section));
@@ -906,7 +904,7 @@
   handler().ScrollToSection(CustomizeChromeSection::kAppearance);
   mock_page_.FlushForTesting();
 
-  EXPECT_EQ(side_panel::mojom::CustomizeChromeSection::kAppearance, section);
+  EXPECT_EQ(CustomizeChromeSection::kAppearance, section);
 }
 
 // Ensures that url's are correctly mapped to their NewTabPage type.
@@ -951,7 +949,7 @@
 }
 
 TEST_F(CustomizeChromePageHandlerTest, UpdateScrollToSection) {
-  side_panel::mojom::CustomizeChromeSection section;
+  CustomizeChromeSection section;
   EXPECT_CALL(mock_page_, ScrollToSection)
       .Times(2)
       .WillRepeatedly(SaveArg<0>(&section));
@@ -960,7 +958,7 @@
   handler().UpdateScrollToSection();
   mock_page_.FlushForTesting();
 
-  EXPECT_EQ(side_panel::mojom::CustomizeChromeSection::kAppearance, section);
+  EXPECT_EQ(CustomizeChromeSection::kAppearance, section);
 }
 
 class CustomizeChromePageHandlerWallpaperSearchTest
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_ui_browsertest.cc b/chrome/browser/ui/webui/tab_search/tab_search_ui_browsertest.cc
index 43ecdda1..beca7c6 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/tab_search/tab_search_ui_browsertest.cc
@@ -119,7 +119,8 @@
   ASSERT_EQ(tab_id, GetActiveTab()->GetHandle());
 }
 
-IN_PROC_BROWSER_TEST_F(TabSearchUIBrowserTest, CloseTabAction) {
+// TODO(https://crbug.com/401303184): Disabled due to excessive flakiness.
+IN_PROC_BROWSER_TEST_F(TabSearchUIBrowserTest, DISABLED_CloseTabAction) {
   ASSERT_EQ(4, browser()->tab_strip_model()->GetTabCount());
 
   tabs::TabHandle tab_id =
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_file_handling_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_file_handling_browsertest.cc
index 83e117f4..b0eb55dd 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_file_handling_browsertest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_file_handling_browsertest.cc
@@ -4,10 +4,8 @@
 
 #include "base/files/file_path.h"
 #include "base/test/bind.h"
+#include "base/test/test_future.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
-#include "chrome/browser/apps/app_service/app_service_proxy.h"
-#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/extensions/scoped_test_mv2_enabler.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
@@ -61,10 +59,15 @@
     params.launch_files = launch_files;
     params.override_url = url;
 
-    content::WebContents* web_contents =
-        apps::AppServiceProxyFactory::GetForProfile(profile())
-            ->BrowserAppLauncher()
-            ->LaunchAppWithParamsForTesting(std::move(params));
+    web_app::WebAppProvider* provider =
+        web_app::WebAppProvider::GetForLocalAppsUnchecked(profile());
+    base::test::TestFuture<base::WeakPtr<Browser>,
+                           base::WeakPtr<content::WebContents>,
+                           apps::LaunchContainer>
+        future;
+    provider->scheduler().LaunchAppWithCustomParams(std::move(params),
+                                                    future.GetCallback());
+    auto* web_contents = future.template Get<1>().get();
 
     content::WaitForLoadStop(web_contents);
 
diff --git a/chrome/browser/web_applications/isolated_web_apps/update/isolated_web_app_update_manager_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/update/isolated_web_app_update_manager_browsertest.cc
index e3fb144..1558d8f 100644
--- a/chrome/browser/web_applications/isolated_web_apps/update/isolated_web_app_update_manager_browsertest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/update/isolated_web_app_update_manager_browsertest.cc
@@ -39,6 +39,7 @@
 #include "chrome/browser/web_applications/isolated_web_apps/install/isolated_web_app_install_source.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_trust_checker.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolation_data.h"
 #include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_installer.h"
 #include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_constants.h"
 #include "chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.h"
@@ -57,7 +58,10 @@
 #include "components/component_updater/component_updater_paths.h"
 #include "components/prefs/pref_service.h"
 #include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
+#include "components/webapps/isolated_web_apps/features.h"
+#include "components/webapps/isolated_web_apps/iwa_key_distribution_info_provider.h"
 #include "components/webapps/isolated_web_apps/test_support/signing_keys.h"
+#include "components/webapps/isolated_web_apps/types/iwa_version.h"
 #include "components/webapps/isolated_web_apps/types/storage_location.h"
 #include "components/webapps/isolated_web_apps/types/update_channel.h"
 #include "content/public/browser/browsing_data_remover.h"
@@ -216,9 +220,19 @@
             .AddJs("/register-sw.js", kRegisterServiceWorkerScript)
             .AddJs("/sw.js", kServiceWorkerScript)
             .BuildBundle(GetWebBundleId(), {test::GetDefaultEd25519KeyPair()}));
+
+    // TODO(crbug.com/426542051): Remove this when more versatile approach is
+    // implemented.
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    ASSERT_THAT(test::UpdateKeyDistributionInfoWithAllowlist(
+                    base::Version("1.0.0"),
+                    /*managed_allowlist=*/{GetWebBundleId()}),
+                HasValue());
   }
 
   IsolatedWebAppTestUpdateServer iwa_test_update_server_;
+  base::test::ScopedFeatureList features_{
+      features::kIsolatedWebAppManagedAllowlist};
 };
 
 IN_PROC_BROWSER_TEST_F(IsolatedWebAppUpdateManagerBrowserTest, Succeeds) {
@@ -262,6 +276,59 @@
                                     /*expected_count=*/0);
 }
 
+// The case of allowlisted app being installed and updated is covered by
+// IsolatedWebAppUpdateManagerBrowserTest::Succeeds
+IN_PROC_BROWSER_TEST_F(IsolatedWebAppUpdateManagerBrowserTest,
+                       NoUpdatesWhenAppNotAllowlisted) {
+  base::HistogramTester histogram_tester;
+
+  profile()->GetPrefs()->SetList(
+      prefs::kIsolatedWebAppInstallForceList,
+      base::Value::List().Append(
+          iwa_test_update_server_.CreateForceInstallPolicyEntry(
+              GetWebBundleId())));
+
+  web_app::WebAppTestInstallObserver(browser()->profile())
+      .BeginListeningAndWait({GetAppId()});
+
+  ASSERT_OK_AND_ASSIGN(const IsolationData& app_isolation_data,
+                       GetIsolatedWebApp(GetAppId())->isolation_data());
+  IwaVersion installed_version = app_isolation_data.version();
+
+  // Clear the allowlist, so the app is not allowlisted
+  {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    ASSERT_THAT(
+        test::UpdateKeyDistributionInfoWithAllowlist(base::Version("1.0.1"),
+                                                     /*managed_allowlist=*/{}),
+        HasValue());
+  }
+
+  AddNewBundleToUpdateServer("app-7.0.6", "7.0.6");
+
+  ASSERT_FALSE(
+      IwaKeyDistributionInfoProvider::GetInstance().IsManagedUpdatePermitted(
+          GetWebBundleId().id()));
+  EXPECT_THAT(provider().iwa_update_manager().DiscoverUpdatesNow(), Eq(0ul));
+
+  histogram_tester.ExpectBucketCount(
+      kIwaKeyDistributionManagedUpdateAllowedHistogramName, /*sample=*/false,
+      /*expected_count=*/2);
+  histogram_tester.ExpectBucketCount(
+      kIwaKeyDistributionManagedUpdateAllowedHistogramName, /*sample=*/true,
+      /*expected_count=*/0);
+
+  histogram_tester.ExpectBucketCount("WebApp.Isolated.UpdateSuccess",
+                                     /*sample=*/true, /*expected_count=*/0);
+  histogram_tester.ExpectBucketCount("WebApp.Isolated.UpdateSuccess",
+                                     /*sample=*/false, /*expected_count=*/0);
+  histogram_tester.ExpectTotalCount("WebApp.Isolated.UpdateError",
+                                    /*expected_count=*/0);
+
+  // Check the version has not changed
+  EXPECT_EQ(app_isolation_data.version(), installed_version);
+}
+
 IN_PROC_BROWSER_TEST_F(IsolatedWebAppUpdateManagerBrowserTest,
                        NoUpdatesToIwaPinnedToCurrentVersion) {
   base::HistogramTester histogram_tester;
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager.cc b/chrome/browser/web_applications/policy/web_app_policy_manager.cc
index b3612c5..0beba65 100644
--- a/chrome/browser/web_applications/policy/web_app_policy_manager.cc
+++ b/chrome/browser/web_applications/policy/web_app_policy_manager.cc
@@ -1044,6 +1044,9 @@
       case policy::SystemFeature::kCalculator:
         disabled_web_apps_.insert(ash::kCalculatorAppId);
         break;
+      case policy::SystemFeature::kVids:
+        disabled_web_apps_.insert(ash::kVidsAppId);
+        break;
       case policy::SystemFeature::kUnknownSystemFeature:
       case policy::SystemFeature::kBrowserSettings:
       case policy::SystemFeature::kWebStore:
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_controller.cc b/chrome/browser/webauthn/authenticator_request_dialog_controller.cc
index 507953cf..a54dc95 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_controller.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_controller.cc
@@ -249,7 +249,8 @@
         (transport_availability.transport_list_did_include_internal ||
          transport_availability.has_empty_allow_list) &&
         transport_availability.has_platform_authenticator_credential ==
-            device::FidoRequestHandlerBase::RecognizedCredential::kUnknown;
+            device::FidoRequestHandlerBase::RecognizedCredential::kUnknown &&
+        transport_availability.win_is_uvpaa;
     win_handles_hybrid =
         (transport_availability.transport_list_did_include_hybrid ||
          transport_availability.has_empty_allow_list) &&
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc b/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc
index b4cc1ca..2469ab4 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc
@@ -2603,57 +2603,65 @@
   bool has_hybrid;
   bool has_internal;
   bool supports_hybrid;
+  bool has_uvpaa;
   HasCreds has_creds;
   int expected_button;
 } kWinHelloButtonGetAssertionTestCases[] = {
     // Windows v7+ with all transports.
-    {L, true, true, true, true, HasCreds::kHasRecognizedCredential, kPhoneOrSk},
+    {L, true, true, true, true, true, HasCreds::kHasRecognizedCredential,
+     kPhoneOrSk},
 
     // Windows v7+ with only security keys.
-    {L, true, false, false, true, HasCreds::kNoRecognizedCredential, kSk},
+    {L, true, false, false, true, true, HasCreds::kNoRecognizedCredential, kSk},
 
     // Windows v7+ with only phones.
-    {L, false, true, false, true, HasCreds::kNoRecognizedCredential, kPhone},
+    {L, false, true, false, true, true, HasCreds::kNoRecognizedCredential,
+     kPhone},
 
     // Windows v7+ with only internal creds.
-    {L, false, false, true, true, HasCreds::kHasRecognizedCredential,
+    {L, false, false, true, true, true, HasCreds::kHasRecognizedCredential,
      kNoChromeUI},
 
     // Windows v7+ with empty allow-list.
-    {L, false, false, false, true, HasCreds::kHasRecognizedCredential,
+    {L, false, false, false, true, true, HasCreds::kHasRecognizedCredential,
      kPhoneOrSk},
 
     // Windows v5+ with all transports.
-    {L, true, true, true, false, HasCreds::kHasRecognizedCredential, kSk},
+    {L, true, true, true, false, true, HasCreds::kHasRecognizedCredential, kSk},
 
     // Windows v5+ with only security keys
-    {L, true, false, false, false, HasCreds::kNoRecognizedCredential, kSk},
+    {L, true, false, false, false, true, HasCreds::kNoRecognizedCredential,
+     kSk},
 
     // Windows v5+ with only phones.
-    {L, false, true, false, false, HasCreds::kNoRecognizedCredential,
+    {L, false, true, false, false, true, HasCreds::kNoRecognizedCredential,
      kNoWinButton},
 
     // Windows v5+ with only internal creds.
-    {L, false, false, true, false, HasCreds::kHasRecognizedCredential,
+    {L, false, false, true, false, true, HasCreds::kHasRecognizedCredential,
      kNoChromeUI},
 
     // Windows v5+ with empty allow-list.
-    {L, false, false, false, false, HasCreds::kHasRecognizedCredential, kSk},
+    {L, false, false, false, false, true, HasCreds::kHasRecognizedCredential,
+     kSk},
 
     // Windows <v4 with all transports.
-    {L, true, true, true, false, HasCreds::kUnknown, kHelloOrSk},
+    {L, true, true, true, false, true, HasCreds::kUnknown, kHelloOrSk},
 
     // Windows <v4 with only security keys.
-    {L, true, false, false, false, HasCreds::kUnknown, kSk},
+    {L, true, false, false, false, true, HasCreds::kUnknown, kSk},
 
     // Windows <v4 with only phones.
-    {L, false, true, false, false, HasCreds::kUnknown, kNoWinButton},
+    {L, false, true, false, false, true, HasCreds::kUnknown, kNoWinButton},
 
     // Windows <v4 with only internal creds.
-    {L, false, false, true, false, HasCreds::kUnknown, kHello},
+    {L, false, false, true, false, true, HasCreds::kUnknown, kHello},
 
     // Windows <v4 with empty allow-list.
-    {L, false, false, false, false, HasCreds::kUnknown, kHelloOrSk},
+    {L, false, false, false, false, true, HasCreds::kUnknown, kHelloOrSk},
+
+    // Windows <v4 with empty allow-list and no Win Hello.
+    {L, false, false, false, false, false, HasCreds::kUnknown, kSk},
 };
 #undef L
 
@@ -2676,6 +2684,7 @@
     transports_info.transport_list_did_include_internal =
         test_case.has_internal;
     transports_info.has_platform_authenticator_credential = test_case.has_creds;
+    transports_info.win_is_uvpaa = test_case.has_uvpaa;
     if (test_case.has_creds == HasCreds::kHasRecognizedCredential) {
       transports_info.recognized_credentials = {kCred1};
     }
@@ -2687,6 +2696,7 @@
     SCOPED_TRACE(testing::Message() << "SK: " << test_case.has_sk);
     SCOPED_TRACE(testing::Message() << "Hybrid: " << test_case.has_hybrid);
     SCOPED_TRACE(testing::Message() << "Internal: " << test_case.has_internal);
+    SCOPED_TRACE(testing::Message() << "Win isUVPAA: " << test_case.has_uvpaa);
     SCOPED_TRACE(testing::Message()
                  << "Has creds: " << static_cast<int>(test_case.has_creds));
     SCOPED_TRACE(testing::Message()
diff --git a/chrome/browser/win/installer_downloader/installer_downloader_feature.cc b/chrome/browser/win/installer_downloader/installer_downloader_feature.cc
index 5d7b12e..b59bf29 100644
--- a/chrome/browser/win/installer_downloader/installer_downloader_feature.cc
+++ b/chrome/browser/win/installer_downloader/installer_downloader_feature.cc
@@ -8,6 +8,6 @@
 
 namespace installer_downloader {
 
-BASE_FEATURE(kInstallerDownloader, base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kInstallerDownloader, base::FEATURE_ENABLED_BY_DEFAULT);
 
 }  // namespace installer_downloader
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index abbb9e2..9b0bc1b 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1759621224-c71acd3bc1f2424958381d400a1df254dc787cc1-ce8d3653e3dbd0eee0615a3b9a99cb8f6db3b1e0.profdata
+chrome-android32-main-1759794928-6bcfb547996fbacc62d5866f6c4f049202cead9c-9bf583590cbae348394992d0625f5266fa0dd7c1.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index f365a87..81c1f1c 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1759632511-a48181f1da89f0b454093929e28d7fc27adf4560-d714a384b26a507c96f229b6bfdce40bb3c89f17.profdata
+chrome-android64-main-1759819861-a8c7b22f53a1e8e5601fb0bc3e0cc1f08a5d2d95-ef851708689064579ae6631b2ff39930c07293a2.profdata
diff --git a/chrome/build/android-desktop-x64.pgo.txt b/chrome/build/android-desktop-x64.pgo.txt
index 3788d9e..8f3a4691 100644
--- a/chrome/build/android-desktop-x64.pgo.txt
+++ b/chrome/build/android-desktop-x64.pgo.txt
@@ -1 +1 @@
-chrome-android-desktop-x64-main-1759621224-3e3363b66b789a6b48deb496cbe529f254bf37e0-ce8d3653e3dbd0eee0615a3b9a99cb8f6db3b1e0.profdata
+chrome-android-desktop-x64-main-1759816770-bc9b7f2767a75f1b7ca9cf9d0800af27c19aa083-d69f88fbea811e1944ad7fab25a36712341f19e6.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 29ce9d1..8cbd600 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1759621224-f44e454952c6c3cae3b6ecd45c30f96c964d9c2e-ce8d3653e3dbd0eee0615a3b9a99cb8f6db3b1e0.profdata
+chrome-linux-main-1759816770-10a1f93a69305d8d7b1da05596ddebf5c1644fa7-d69f88fbea811e1944ad7fab25a36712341f19e6.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index be80da9..03419834 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1759635079-adf565bf8b1f2f55c06c0627530da8e4e10b92e2-59ed3253ea9bf2f408c68e7f767728fc2c389df4.profdata
+chrome-mac-arm-main-1759823925-5eb5e53bce6489c801088d0711307417c0063854-33ea5b876f1a1582001c2829d236cc989fc32276.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index d51a81c..56c128dc 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1759621224-bddcddfed3d26f0a54022fb47e4ff309095de096-ce8d3653e3dbd0eee0615a3b9a99cb8f6db3b1e0.profdata
+chrome-mac-main-1759816770-8a42f26f04a180d7df98e376e570537a30bf9f1a-d69f88fbea811e1944ad7fab25a36712341f19e6.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 333e17e..2cd91a3 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1759610952-46881dc30140c66237fa3fd8acd598c8663c2a7b-bac2efef72cbddb1e64e7e07bb03fb122f2dd9f4.profdata
+chrome-win32-main-1759794928-7c263c3c0045a775fe26b13c2ab710b46026e7c1-9bf583590cbae348394992d0625f5266fa0dd7c1.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 26c7bc1..85aaf16 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1759621224-68274e107a06c4e3ab976b8fe9afcb7ed4897c5f-ce8d3653e3dbd0eee0615a3b9a99cb8f6db3b1e0.profdata
+chrome-win64-main-1759794928-e3426ecd406c5300827e86fa293fc88ceb863f3e-9bf583590cbae348394992d0625f5266fa0dd7c1.profdata
diff --git a/chrome/chrome_elf/third_party_dlls/hook.cc b/chrome/chrome_elf/third_party_dlls/hook.cc
index 0f9b6da7..0cfa9c1 100644
--- a/chrome/chrome_elf/third_party_dlls/hook.cc
+++ b/chrome/chrome_elf/third_party_dlls/hook.cc
@@ -405,8 +405,9 @@
   // Setup() applies the system-service patch, and stores a copy of the original
   // system service coded in |g_thunk_storage|.
   NTSTATUS ntstatus = thunk.Setup(
-      ::GetModuleHandle(kNtdllName), "NtMapViewOfSection", entry_point,
-      g_thunk_storage, sizeof(g_thunk_storage), nullptr);
+      ::GetModuleHandle(kNtdllName), reinterpret_cast<void*>(&__ImageBase),
+      "NtMapViewOfSection", nullptr, entry_point, g_thunk_storage,
+      sizeof(g_thunk_storage), nullptr);
 
   if (!NT_SUCCESS(ntstatus)) {
     // Remember the status code.
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index dc2467c..ed02ca47 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -291,6 +291,9 @@
 // Controls whether the actor component of Glic is enabled.
 BASE_FEATURE(kGlicActor, base::FEATURE_ENABLED_BY_DEFAULT);
 
+const base::FeatureParam<base::TimeDelta> kGlicActorClickDelay{
+    &kGlicActor, "glic-actor-click-delay", base::Milliseconds(5)};
+
 // Controls whether the Actor UI components are enabled.
 BASE_FEATURE(kGlicActorUi, base::FEATURE_ENABLED_BY_DEFAULT);
 
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 5dc92a1..08bdb80 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -222,6 +222,8 @@
 COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kGeoLanguage);
 
 COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kGlicActor);
+COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::FeatureParam<base::TimeDelta> kGlicActorClickDelay;
 COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kGlicActorUi);
 
 COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/common/extensions/api/login_state.idl b/chrome/common/extensions/api/login_state.idl
index e6835826..9999cd5f1 100644
--- a/chrome/common/extensions/api/login_state.idl
+++ b/chrome/common/extensions/api/login_state.idl
@@ -12,7 +12,10 @@
     SIGNIN_PROFILE,
 
     // Specifies that the extension is in the user profile.
-    USER_PROFILE
+    USER_PROFILE,
+
+    // Specifies that the extension is in the lock screen profile.
+    [nodoc] LOCK_PROFILE
   };
 
   enum SessionState {
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index a8b5059..ac7f0f3 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -381,9 +381,16 @@
 inline constexpr char kInsecureDownloadBlockingLearnMoreUrl[] =
     "https://support.google.com/chrome?p=mixed_content_downloads";
 
-// "myactivity.google.com" URL for the history checkbox in ClearBrowsingData.
+// "myactivity.google.com" URLs with their respective UTM sources.
+// - In the Clear Browsing Data footer.
+// - In the Clear Browsing Data "notice about other forms of history".
+// - On the history page.
 inline constexpr char16_t kMyActivityUrlInClearBrowsingData[] =
     u"https://myactivity.google.com/myactivity?utm_source=chrome_cbd";
+inline constexpr char16_t kMyActivityUrlInClearBrowsingDataNotice[] =
+    u"https://myactivity.google.com/myactivity/?utm_source=chrome_n";
+inline constexpr char16_t kMyActivityUrlInHistory[] =
+    u"https://myactivity.google.com/myactivity/?utm_source=chrome_h";
 
 // The URL for the Gemini Personal Context page.
 inline constexpr char16_t kGeminiPersonalContextUrl[] =
diff --git a/chrome/installer/util/install_service_work_item_impl.cc b/chrome/installer/util/install_service_work_item_impl.cc
index d815f89..1ef991c 100644
--- a/chrome/installer/util/install_service_work_item_impl.cc
+++ b/chrome/installer/util/install_service_work_item_impl.cc
@@ -634,15 +634,18 @@
 
 bool InstallServiceWorkItemImpl::InstallNewService() {
   DCHECK(!service_.IsValid());
-  bool success = InstallService(
-      ServiceConfig(kServiceType, start_type_, kServiceErrorControl,
-                    service_cmd_line_.GetCommandLineString(),
-                    kServiceDependencies, GetCurrentServiceDisplayName()));
-  if (success)
-    rollback_new_service_ = true;
+  if (!InstallService(ServiceConfig(
+          kServiceType, start_type_, kServiceErrorControl,
+          service_cmd_line_.GetCommandLineString(), kServiceDependencies,
+          GetCurrentServiceDisplayName()))) {
+    return false;
+  }
+
+  rollback_new_service_ = true;
 
   SetDescription();
-  return success;
+
+  return true;
 }
 
 bool InstallServiceWorkItemImpl::UpgradeService() {
diff --git a/chrome/renderer/actor/click_tool.cc b/chrome/renderer/actor/click_tool.cc
index 2a6c0be..c1f87cf 100644
--- a/chrome/renderer/actor/click_tool.cc
+++ b/chrome/renderer/actor/click_tool.cc
@@ -7,6 +7,7 @@
 #include <cstdint>
 #include <optional>
 
+#include "base/functional/bind.h"
 #include "base/strings/to_string.h"
 #include "base/time/time.h"
 #include "base/types/expected.h"
@@ -88,8 +89,14 @@
   journal_->Log(task_id_, "ClickTool::Execute",
                 JournalDetailsBuilder().Add("point", click_point).Build());
 
-  mojom::ActionResultPtr result = CreateAndDispatchClick(
-      button, click_count, click_point, frame_->GetWebFrame()->FrameWidget());
+  CreateAndDispatchClick(
+      button, click_count, click_point, weak_ptr_factory_.GetWeakPtr(),
+      base::BindOnce(&ClickTool::OnActionComplete,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void ClickTool::OnActionComplete(ToolFinishedCallback callback,
+                                 mojom::ActionResultPtr result) {
   std::move(callback).Run(std::move(result));
 }
 
diff --git a/chrome/renderer/actor/click_tool.h b/chrome/renderer/actor/click_tool.h
index e3d36cb..004939ff 100644
--- a/chrome/renderer/actor/click_tool.h
+++ b/chrome/renderer/actor/click_tool.h
@@ -8,6 +8,7 @@
 #include <cstdint>
 
 #include "base/memory/raw_ref.h"
+#include "base/memory/weak_ptr.h"
 #include "base/types/expected.h"
 #include "chrome/common/actor.mojom.h"
 #include "chrome/common/actor/task_id.h"
@@ -43,7 +44,12 @@
   using ValidatedResult = base::expected<gfx::PointF, mojom::ActionResultPtr>;
   ValidatedResult Validate() const;
 
+  void OnActionComplete(ToolFinishedCallback callback,
+                        mojom::ActionResultPtr result);
+
   mojom::ClickActionPtr action_;
+
+  base::WeakPtrFactory<ClickTool> weak_ptr_factory_{this};
 };
 
 }  // namespace actor
diff --git a/chrome/renderer/actor/tool_base.cc b/chrome/renderer/actor/tool_base.cc
index 6f62ddb..225212e 100644
--- a/chrome/renderer/actor/tool_base.cc
+++ b/chrome/renderer/actor/tool_base.cc
@@ -182,7 +182,7 @@
 
     // Target node for coordinate target is obtained through blink hit test
     // which includes shadow host elements.
-    if (!observed_target_node.ContainsIncludingHostElements(&target_node)) {
+    if (!observed_target_node.ContainsViaFlatTree(&target_node)) {
       journal_->Log(
           task_id_, "TimeOfUseValidation",
           JournalDetailsBuilder()
@@ -214,8 +214,9 @@
             resolved_target.point);
     const blink::WebElement hit_element = hit_test_result.GetElement();
     // The action target from APC is not as granular as the live DOM hit test.
-    // Include shadow host element as the hit test would land on those.
-    if (!target_node.ContainsIncludingHostElements(&hit_element)) {
+    // Include shadow host element as the hit test would land on those. Also
+    // check if the hit element was pulled in via a Web Components slot.
+    if (!target_node.ContainsViaFlatTree(&hit_element)) {
       journal_->Log(task_id_, "TimeOfUseValidation",
                     JournalDetailsBuilder()
                         .Add("target_id", target_node.GetDomNodeId())
diff --git a/chrome/renderer/actor/tool_base.h b/chrome/renderer/actor/tool_base.h
index 2040486..899d316 100644
--- a/chrome/renderer/actor/tool_base.h
+++ b/chrome/renderer/actor/tool_base.h
@@ -72,6 +72,8 @@
   // interactions.
   virtual bool SupportsPaintStability() const;
 
+  content::RenderFrame* frame() const { return &frame_.get(); }
+
  protected:
   // Raw ref since this is owned by ToolExecutor whose lifetime is tied to
   // RenderFrame.
diff --git a/chrome/renderer/actor/tool_executor.cc b/chrome/renderer/actor/tool_executor.cc
index 2e35582..7399a2fa 100644
--- a/chrome/renderer/actor/tool_executor.cc
+++ b/chrome/renderer/actor/tool_executor.cc
@@ -36,7 +36,9 @@
 namespace actor {
 
 ToolExecutor::ToolExecutor(RenderFrame* frame, Journal& journal)
-    : frame_(*frame), journal_(journal) {}
+    : frame_(*frame), journal_(journal) {
+  CHECK(base::FeatureList::IsEnabled(features::kGlicActor));
+}
 
 ToolExecutor::~ToolExecutor() {
   if (completion_callback_) {
diff --git a/chrome/renderer/actor/tool_utils.cc b/chrome/renderer/actor/tool_utils.cc
index 9303df6..6f2097e 100644
--- a/chrome/renderer/actor/tool_utils.cc
+++ b/chrome/renderer/actor/tool_utils.cc
@@ -6,9 +6,14 @@
 
 #include <sstream>
 
+#include "base/feature_list.h"
+#include "base/functional/bind.h"
 #include "base/strings/strcat.h"
+#include "base/task/sequenced_task_runner.h"
 #include "chrome/common/actor.mojom.h"
 #include "chrome/common/actor/action_result.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/renderer/actor/tool_base.h"
 #include "content/public/renderer/render_frame.h"
 #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
 #include "third_party/blink/public/web/web_element.h"
@@ -21,10 +26,6 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/latency/latency_info.h"
 
-namespace {
-constexpr base::TimeDelta kClickDelay = base::Milliseconds(50);
-}
-
 namespace actor {
 
 using ::blink::WebCoalescedInputEvent;
@@ -117,10 +118,23 @@
   return !rect.IsEmpty();
 }
 
-mojom::ActionResultPtr CreateAndDispatchClick(WebMouseEvent::Button button,
-                                              int count,
-                                              const gfx::PointF& click_point,
-                                              WebFrameWidget* widget) {
+void CreateAndDispatchClick(
+    WebMouseEvent::Button button,
+    int count,
+    const gfx::PointF& click_point,
+    base::WeakPtr<ToolBase> tool,
+    base::OnceCallback<void(mojom::ActionResultPtr)> on_complete) {
+  if (!tool) {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(on_complete),
+                       MakeResult(mojom::ActionResultCode::kExecutorDestroyed,
+                                  /*requires_page_stabilization=*/true,
+                                  "Tool destroyed before click.")));
+    return;
+  }
+
+  WebFrameWidget* widget = tool->frame()->GetWebFrame()->FrameWidget();
   WebMouseEvent mouse_down(WebInputEvent::Type::kMouseDown,
                            WebInputEvent::kNoModifiers, ui::EventTimeForNow());
   mouse_down.button = button;
@@ -137,25 +151,54 @@
       WebCoalescedInputEvent(mouse_down, ui::LatencyInfo()));
 
   if (result == WebInputEventResult::kHandledSuppressed) {
-    return MakeResult(mojom::ActionResultCode::kClickSuppressed,
-                      /*requires_page_stabilization=*/false);
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(on_complete),
+                       MakeResult(mojom::ActionResultCode::kClickSuppressed,
+                                  /*requires_page_stabilization=*/false)));
+    return;
   }
 
   mouse_up.SetType(WebInputEvent::Type::kMouseUp);
-  mouse_up.SetTimeStamp(mouse_down.TimeStamp() + kClickDelay);
 
-  // TODO(crbug.com/402082828): Delay the mouse up to simulate natural click
-  // after ToolExecutor lifetime update.
+  const base::TimeDelta delay = features::kGlicActorClickDelay.Get();
 
-  result = widget->HandleInputEvent(
-      WebCoalescedInputEvent(std::move(mouse_up), ui::LatencyInfo()));
-
-  if (result == WebInputEventResult::kHandledSuppressed) {
-    return MakeResult(mojom::ActionResultCode::kClickSuppressed,
-                      /*requires_page_stabilization=*/true);
-  }
-
-  return MakeOkResult();
+  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](blink::WebMouseEvent mouse_up, base::WeakPtr<ToolBase> tool,
+             base::OnceCallback<void(mojom::ActionResultPtr)> on_complete) {
+            if (!tool) {
+              std::move(on_complete)
+                  .Run(MakeResult(mojom::ActionResultCode::kExecutorDestroyed,
+                                  /*requires_page_stabilization=*/true,
+                                  "Tool destroyed before mouse up."));
+              return;
+            }
+            blink::WebLocalFrame* web_frame = tool->frame()->GetWebFrame();
+            if (!web_frame || !web_frame->FrameWidget()) {
+              std::move(on_complete)
+                  .Run(MakeResult(
+                      mojom::ActionResultCode::kFrameWentAway,
+                      /*requires_page_stabilization=*/false,
+                      "WebFrame or WebFrameWidget was null before mouse up."));
+              return;
+            }
+            mouse_up.SetTimeStamp(ui::EventTimeForNow());
+            WebInputEventResult result =
+                web_frame->FrameWidget()->HandleInputEvent(
+                    WebCoalescedInputEvent(std::move(mouse_up),
+                                           ui::LatencyInfo()));
+            if (result == WebInputEventResult::kHandledSuppressed) {
+              std::move(on_complete)
+                  .Run(MakeResult(mojom::ActionResultCode::kClickSuppressed,
+                                  /*requires_page_stabilization=*/true));
+              return;
+            }
+            std::move(on_complete).Run(MakeOkResult());
+          },
+          std::move(mouse_up), std::move(tool), std::move(on_complete)),
+      delay);
 }
 
 std::string NodeToDebugSring(const blink::WebNode& node) {
diff --git a/chrome/renderer/actor/tool_utils.h b/chrome/renderer/actor/tool_utils.h
index 8b3cdc6d..10f6b106 100644
--- a/chrome/renderer/actor/tool_utils.h
+++ b/chrome/renderer/actor/tool_utils.h
@@ -9,13 +9,14 @@
 #include <optional>
 #include <string>
 
+#include "base/functional/callback_forward.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/common/actor.mojom-forward.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
 #include "third_party/blink/public/common/input/web_mouse_event.h"
 
 namespace blink {
 class WebNode;
-class WebFrameWidget;
 }  // namespace blink
 
 namespace content {
@@ -28,6 +29,8 @@
 
 namespace actor {
 
+class ToolBase;
+
 // Returns the Blink node for the given DOMNodeId if one exists and its document
 // has the given frame as a local root. Returns a null WebNode otherwise.
 blink::WebNode GetNodeFromId(const content::RenderFrame& local_root_frame,
@@ -54,11 +57,12 @@
 
 // Create and dispatch the mouse down event and corresponding mouse up, click
 // event to the widget.
-mojom::ActionResultPtr CreateAndDispatchClick(
+void CreateAndDispatchClick(
     blink::WebMouseEvent::Button button,
     int count,
     const gfx::PointF& click_point,
-    blink::WebFrameWidget* widget);
+    base::WeakPtr<ToolBase> tool,
+    base::OnceCallback<void(mojom::ActionResultPtr)> on_complete);
 
 // Converts Node to a debug string of tag name, id and class.
 std::string NodeToDebugSring(const blink::WebNode& node);
diff --git a/chrome/renderer/actor/type_tool.cc b/chrome/renderer/actor/type_tool.cc
index ae808ba..6c6e524 100644
--- a/chrome/renderer/actor/type_tool.cc
+++ b/chrome/renderer/actor/type_tool.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/feature_list.h"
+#include "base/functional/bind.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/no_destructor.h"
 #include "base/notimplemented.h"
@@ -308,10 +309,17 @@
   gfx::PointF coordinate = validated_result->target;
   journal_->Log(task_id_, "TypeTool::Execute::Focus",
                 JournalDetailsBuilder().Add("coord", coordinate).Build());
-  mojom::ActionResultPtr click_result =
-      CreateAndDispatchClick(blink::WebMouseEvent::Button::kLeft, 1, coordinate,
-                             frame_->GetWebFrame()->FrameWidget());
+  CreateAndDispatchClick(
+      blink::WebMouseEvent::Button::kLeft, 1, coordinate,
+      weak_ptr_factory_.GetWeakPtr(),
+      base::BindOnce(&TypeTool::OnFocusingClickComplete,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     std::move(validated_result), std::move(callback)));
+}
 
+void TypeTool::OnFocusingClickComplete(ValidatedResult validated_result,
+                                       ToolFinishedCallback callback,
+                                       mojom::ActionResultPtr click_result) {
   // Cancel rest of typing if initial click failed.
   if (!IsOk(*click_result)) {
     journal_->Log(
diff --git a/chrome/renderer/actor/type_tool.h b/chrome/renderer/actor/type_tool.h
index 6f0a36c..6f71122 100644
--- a/chrome/renderer/actor/type_tool.h
+++ b/chrome/renderer/actor/type_tool.h
@@ -86,6 +86,9 @@
       KeyParams key_params);
   mojom::ActionResultPtr SimulateKeyPress(TypeTool::KeyParams params);
 
+  void OnFocusingClickComplete(ValidatedResult validated_result,
+                               ToolFinishedCallback callback,
+                               mojom::ActionResultPtr click_result);
   void ContinueIncrementalTyping(ToolFinishedCallback callback);
 
   mojom::TypeActionPtr action_;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 21e01ca..4594bd1 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -981,10 +981,6 @@
     sources += [
       "../browser/apps/app_service/metrics/website_metrics_browser_test_mixin.cc",
       "../browser/apps/app_service/metrics/website_metrics_browser_test_mixin.h",
-      "../browser/certificate_provider/test_certificate_provider_extension.cc",
-      "../browser/certificate_provider/test_certificate_provider_extension.h",
-      "../browser/certificate_provider/test_certificate_provider_extension_mixin.cc",
-      "../browser/certificate_provider/test_certificate_provider_extension_mixin.h",
       "../browser/chromeos/extensions/login_screen/login_screen_apitest_base.cc",
       "../browser/chromeos/extensions/login_screen/login_screen_apitest_base.h",
       "../browser/enterprise/connectors/analysis/mock_file_transfer_analysis_delegate.cc",
@@ -1393,7 +1389,6 @@
     "../browser/storage/shared_storage_browsertest.cc",
     "../browser/task_manager/mock_web_contents_task_manager.cc",
     "../browser/task_manager/mock_web_contents_task_manager.h",
-    "../browser/tpcd/support/origin_trial_service_browsertest.cc",
     "../browser/tpcd/support/tpcd_support_browsertest.cc",
     "../browser/tpcd/support/validity_service_browsertest.cc",
   ]
@@ -1875,6 +1870,7 @@
       "//chrome:chrome_android_core",
       "//chrome/android:delegate_public_impl_java",
       "//chrome/browser/autofill",
+      "//chrome/browser/autofill:browser_tests",
       "//chrome/browser/auxiliary_search:browser_tests",
       "//chrome/browser/banners:android_browsertests",
       "//chrome/browser/browsing_data:constants",
@@ -4116,6 +4112,7 @@
         "../browser/support_tool/signin_data_collector_browsertest.cc",
         "../browser/ui/ash/quick_answers/ui/quick_answers_pixeltest.cc",
         "../browser/ui/views/mahi/mahi_menu_pixeltest.cc",
+        "../browser/ui/views/web_apps/protocol_handler_picker_interactve_uitest.cc",
         "../browser/ui/views/web_apps/web_app_integration_browsertest_cros.cc",
         "base/ash/web_ui_browser_test_browsertest.cc",
         "data/webui/chromeos/async_gen.cc",
@@ -4921,6 +4918,7 @@
           "//build/config/chromebox_for_meetings:buildflags",
           "//chrome/browser/ash/scanning",
           "//chrome/browser/ash/scanning:test_support",
+          "//chrome/browser/certificate_provider",
           "//chrome/browser/chromeos/extensions/smart_card_provider_private:browser_tests",
           "//chrome/browser/chromeos/extensions/telemetry/api:browser_tests",
           "//chrome/browser/chromeos/extensions/vpn_provider",
@@ -7226,6 +7224,7 @@
     "//components/safe_browsing/content/common:interfaces_shared_cpp_sources",
     "//components/safe_browsing/core/browser",
     "//components/safe_browsing/core/browser:referrer_chain_provider",
+    "//components/safe_browsing/core/browser:safe_browsing_metrics_collector",
     "//components/safe_browsing/core/browser:verdict_cache_manager",
     "//components/safe_browsing/core/browser/db",
     "//components/safe_browsing/core/browser/db:test_database_manager",
@@ -7934,10 +7933,8 @@
       "//chrome/browser/safe_browsing/android:test_support_java",
       "//chrome/browser/safety_hub/android:unit_tests",
       "//chrome/browser/share",
-      "//chrome/browser/tab:unit_tests",
       "//chrome/browser/thumbnail:unit_tests",
       "//chrome/browser/touch_to_fill/autofill/android:public",
-      "//chrome/browser/touch_to_fill/autofill/one_time_tokens/android:unit_tests",
       "//chrome/browser/touch_to_fill/password_manager/no_passkeys/android:public",
       "//chrome/browser/touch_to_fill/password_manager/no_passkeys/android:unit_tests",
       "//chrome/browser/touch_to_fill/password_manager/password_generation/android:unit_tests",
@@ -8407,6 +8404,7 @@
       "//components/omnibox/composebox:test_support",
       "//components/passage_embeddings",
       "//components/passage_embeddings:test_support",
+      "//components/safe_browsing/core/browser:safe_browsing_metrics_collector",
       "//components/tab_groups",
       "//components/webapps/isolated_web_apps",
       "//components/webapps/isolated_web_apps/test_support",
@@ -8760,7 +8758,6 @@
     ]
     sources += [
       "../browser/ash/scalable_iph/scalable_iph_factory_impl_unittest.cc",
-      "../browser/certificate_provider/certificate_provider_service_unittest.cc",
       "../browser/component_updater/cros_component_installer_chromeos_unittest.cc",
       "../browser/component_updater/metadata_table_chromeos_unittest.cc",
       "../browser/device_identity/chromeos/device_oauth2_token_store_chromeos_unittest.cc",
@@ -8984,6 +8981,7 @@
       "//chrome/browser/ash/system_web_apps/apps/media_app",
       "//chrome/browser/ash/system_web_apps/test_support",
       "//chrome/browser/ash/wallpaper_handlers:test_support",
+      "//chrome/browser/certificate_provider:unit_tests",
       "//chrome/browser/chromeos/office_web_app",
       "//chrome/browser/chromeos/policy/dlp",
       "//chrome/browser/chromeos/policy/dlp/test:test_support",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/browser/tab/TabTestUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/browser/tab/TabTestUtils.java
index d1f05d1..514f348 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/browser/tab/TabTestUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/browser/tab/TabTestUtils.java
@@ -156,18 +156,6 @@
     }
 
     /**
-     * Swap {@link WebContents} object being used in a tab.
-     * @param tab {@link Tab} object.
-     * @param webContents {@link WebContents} to swap in.
-     * @param didStartLoad Whether the content started loading.
-     * @param didFinishLoad Whether the content finished loading.
-     */
-    public static void swapWebContents(
-            Tab tab, WebContents webContents, boolean didStartLoad, boolean didFinishLoad) {
-        ((TabImpl) tab).swapWebContents(webContents, didStartLoad, didFinishLoad);
-    }
-
-    /**
      * @param tab {@link Tab} object.
      * @return {@link TabDelegateFactory} for a given tab.
      */
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/CtaPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/CtaPageStation.java
index bbef640..ad7a6999 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/CtaPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/CtaPageStation.java
@@ -18,6 +18,7 @@
 import org.chromium.base.test.transit.ViewSpec;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.incognito.IncognitoUtils;
 import org.chromium.chrome.browser.layouts.LayoutType;
 import org.chromium.chrome.browser.omnibox.UrlBar;
 import org.chromium.chrome.browser.tab.Tab;
@@ -111,6 +112,50 @@
                 .arriveAt(IncognitoNewTabPageStation.newBuilder().initOpeningNewTab().build());
     }
 
+    /**
+     * Shortcut to open a new window programmatically as if selecting "New Window" from the app
+     * menu.
+     */
+    public RegularNewTabPageStation openNewWindowFast() {
+        return ChromeTriggers.invokeCustomMenuActionTo(R.id.new_window_menu_id, this)
+                .inNewTask()
+                .arriveAt(RegularNewTabPageStation.newBuilder().withEntryPoint().build());
+    }
+
+    /**
+     * Shortcut to open a new incognito window programmatically as if selecting "New Incognito
+     * Window" from the app menu.
+     */
+    public IncognitoNewTabPageStation openNewIncognitoWindowFast() {
+        return ChromeTriggers.invokeCustomMenuActionTo(R.id.new_incognito_window_menu_id, this)
+                .inNewTask()
+                .arriveAt(IncognitoNewTabPageStation.newBuilder().withEntryPoint().build());
+    }
+
+    /**
+     * Attempts to open a new tab programmatically as if selecting "New Tab" from the app menu if
+     * available. If not available, attempts to open a new window.
+     */
+    public RegularNewTabPageStation openNewTabOrWindowFast() {
+        if (IncognitoUtils.shouldOpenIncognitoAsWindow() && mIsIncognito) {
+            return openNewWindowFast();
+        } else {
+            return openNewTabFast();
+        }
+    }
+
+    /**
+     * Attempts to open a new incognito tab programmatically as if selecting "New Incognito Tab"
+     * from the app menu if available. If not available, attempts to open a new incognito window.
+     */
+    public IncognitoNewTabPageStation openNewIncognitoTabOrWindowFast() {
+        if (IncognitoUtils.shouldOpenIncognitoAsWindow() && !mIsIncognito) {
+            return openNewIncognitoWindowFast();
+        } else {
+            return openNewIncognitoTabFast();
+        }
+    }
+
     /** Shortcut to select a different tab programmatically. */
     public <T extends CtaPageStation> T selectTabFast(
             Tab tabToSelect, Supplier<Builder<T>> pageStationFactory) {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageAppMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageAppMenuFacility.java
index 8e92e66..597e0dc 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageAppMenuFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageAppMenuFacility.java
@@ -56,11 +56,14 @@
         // TODO: Declare top buttons (forward, reload, bookmark, etc.).
         // TODO: Declare more common menu items
 
-        mNewTab = declareMenuItem(items, NEW_TAB_ID);
-
         if (IncognitoUtils.shouldOpenIncognitoAsWindow()) {
-            mNewIncognitoWindow = declareMenuItem(items, NEW_INCOGNITO_WINDOW_ID);
+            if (!mHostStation.isIncognito()) {
+                mNewTab = declareMenuItem(items, NEW_TAB_ID);
+            } else {
+                mNewIncognitoTab = declareMenuItem(items, NEW_INCOGNITO_TAB_ID);
+            }
         } else {
+            mNewTab = declareMenuItem(items, NEW_TAB_ID);
             mNewIncognitoTab = declareMenuItem(items, NEW_INCOGNITO_TAB_ID);
         }
 
@@ -73,16 +76,27 @@
             mPinTab = declarePossibleMenuItem(items, PIN_TAB);
             mUnpinTab = declarePossibleMenuItem(items, UNPIN_TAB);
         }
+        if (IncognitoUtils.shouldOpenIncognitoAsWindow()) {
+            mNewWindow = declareMenuItem(items, NEW_WINDOW_ID);
+            mNewIncognitoWindow = declareMenuItem(items, NEW_INCOGNITO_WINDOW_ID);
+        }
         mSettings = declareMenuItem(items, SETTINGS_ID);
-
     }
 
-    /** Select "New tab" from the app menu. */
+    /** Select "New tab" from the app menu. If this doesn't exist, select "New window". */
     public RegularNewTabPageStation openNewTab() {
-        return mNewTab.scrollToAndSelectTo().arriveAt(createNewTabPageStation());
+        if (mNewTab != null) {
+            return mNewTab.scrollToAndSelectTo().arriveAt(createNewTabPageStation());
+        } else {
+            assert mNewWindow != null : "App menu does not have 'New tab' or 'New window'";
+            return openNewWindow();
+        }
     }
 
-    /** Select "New Incognito tab" or "New Incognito window" from the app menu. */
+    /**
+     * Select "New Incognito tab" from the app menu. If this doesn't exist, select "New Incognito
+     * window".
+     */
     public IncognitoNewTabPageStation openNewIncognitoTab() {
         if (mNewIncognitoTab != null) {
             return mNewIncognitoTab
@@ -90,11 +104,8 @@
                     .arriveAt(createNewIncognitoTabPageStation());
         } else {
             assert mNewIncognitoWindow != null
-                    : "App menu is expected to show 'New Incognito window' or 'New Incognito tab";
-            return mNewIncognitoWindow
-                    .scrollToAndSelectTo()
-                    .inNewTask()
-                    .arriveAt(createNewIncognitoWindowStation());
+                    : "App menu does not have 'New Incognito tab' or 'New Incognito window'";
+            return openNewIncognitoWindow();
         }
     }
 
@@ -105,6 +116,18 @@
         return mNewWindow.scrollToAndSelectTo().inNewTask().arriveAt(createNewWindowStation());
     }
 
+    /** Select "New Incognito window" from the app menu. */
+    public IncognitoNewTabPageStation openNewIncognitoWindow() {
+        TabbedAppMenuPropertiesDelegate delegate = getTabbedAppMenuPropertiesDelegate();
+        assert delegate.shouldShowNewIncognitoWindow()
+                : "App menu is not expected to show 'New Incognito window'";
+        assert mNewIncognitoWindow != null;
+        return mNewIncognitoWindow
+                .scrollToAndSelectTo()
+                .inNewTask()
+                .arriveAt(createNewIncognitoWindowStation());
+    }
+
     /**
      * Select "Add to group" from the app menu. This opens a bottom sheet to add the current tab to
      * a tab group.
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModel.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModel.java
index dfe1d7c..4314541 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModel.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/tabmodel/MockTabModel.java
@@ -188,7 +188,7 @@
     @Override
     public Tab getTabAt(int position) {
         // Mimic the index safety of TabModelImpl.
-        if (position < 0 || position > mTabs.size()) return null;
+        if (position < 0 || position >= mTabs.size()) return null;
 
         return mTabs.get(position);
     }
diff --git a/chrome/test/chromedriver/chrome_launcher.cc b/chrome/test/chromedriver/chrome_launcher.cc
index 1b63764..acedd65 100644
--- a/chrome/test/chromedriver/chrome_launcher.cc
+++ b/chrome/test/chromedriver/chrome_launcher.cc
@@ -90,6 +90,8 @@
     "allow-pre-commit-input",
     // https://crbug.com/445332809.
     "disable-features=IgnoreDuplicateNavs",
+    // https://crbug.com/431928370.
+    "disable-features=Prewarm",
 };
 
 const char* const kDesktopSwitches[] = {
diff --git a/chrome/test/data/actor/page_with_clickable_element.html b/chrome/test/data/actor/page_with_clickable_element.html
index 1c19707..f71d267 100644
--- a/chrome/test/data/actor/page_with_clickable_element.html
+++ b/chrome/test/data/actor/page_with_clickable_element.html
@@ -69,10 +69,13 @@
   });
 
   let mouse_event_log = [];
+  let mouse_event_timestamps = [];
 
   // Log all mouse events in the format: Event[Element#ID]
-  const log_handler =
-    e => mouse_event_log.push(`${e.type}[${e.target.tagName}#${e.target.id}]`);
+  const log_handler = e => {
+    mouse_event_log.push(`${e.type}[${e.target.tagName}#${e.target.id}]`);
+    mouse_event_timestamps.push(performance.now());
+  };
 
   document.addEventListener('click', log_handler);
   document.addEventListener('mousedown', log_handler);
diff --git a/chrome/test/data/actor/page_with_web_component_button.html b/chrome/test/data/actor/page_with_web_component_button.html
new file mode 100644
index 0000000..02099a0
--- /dev/null
+++ b/chrome/test/data/actor/page_with_web_component_button.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Button Test</title>
+<template id="rt-button-template">
+  <style>
+    button {
+      margin: 0;
+      padding: 0;
+    }
+  </style>
+  <button aria-label="clickable">
+    <slot></slot>
+  </button>
+  <span>Some other text</span>
+</template>
+<script>
+  customElements.define('rt-button', class extends HTMLElement {
+    constructor() {
+      super();
+      const template = document.getElementById('rt-button-template').content;
+      this.attachShadow({mode: 'open'}).appendChild(template.cloneNode(true));
+    }
+  });
+</script>
+</head>
+<body>
+  <rt-button onclick="document.title='Clicked'">
+    <span>Click me</span>
+  </rt-button>
+</body>
+</html>
diff --git a/chrome/test/data/autofill/sms_otp_form.html b/chrome/test/data/autofill/sms_otp_form.html
new file mode 100644
index 0000000..e35ee6e
--- /dev/null
+++ b/chrome/test/data/autofill/sms_otp_form.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <form>
+      <input type="text" name="otp">
+    </form>
+  </body>
+</html>
\ No newline at end of file
diff --git a/chrome/test/data/fingerprinting_protection/canvas_noise_token_shared_worker.js b/chrome/test/data/fingerprinting_protection/canvas_noise_token_shared_worker.js
new file mode 100644
index 0000000..5406c64b
--- /dev/null
+++ b/chrome/test/data/fingerprinting_protection/canvas_noise_token_shared_worker.js
@@ -0,0 +1,13 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+self.addEventListener('connect', (event) => {
+  const port = event.ports[0];
+  port.addEventListener('message', (event) => {
+    if (event.data === 'get-canvas-noise-token') {
+      const token = CanvasInterventionsTest.getCanvasNoiseToken();
+      port.postMessage(token);
+    }
+  });
+  port.start();
+});
diff --git a/chrome/test/data/pdf/BUILD.gn b/chrome/test/data/pdf/BUILD.gn
index 2cf4715..7e4f3038 100644
--- a/chrome/test/data/pdf/BUILD.gn
+++ b/chrome/test/data/pdf/BUILD.gn
@@ -78,6 +78,14 @@
       "selectable_icon_button_test.ts",
     ]
 
+    if (enable_pdf_save_to_drive) {
+      files += [
+        "ink2_save_to_drive_cancelled_before_unload_test.ts",
+        "ink2_save_to_drive_success_before_unload_test.ts",
+        "ink2_save_to_drive_uploading_more_edits_before_unload_test.ts",
+      ]
+    }
+
     if (!is_chromeos) {
       files += [ "ink2_disabled_test.ts" ]
     }
@@ -86,12 +94,18 @@
   if (enable_pdf_save_to_drive) {
     files += [
       "circular_progress_ring_test.ts",
+      "save_to_drive_before_unload_uploading_test.ts",
       "save_to_drive_bubble_test.ts",
       "save_to_drive_controls_test.ts",
       "save_to_drive_test.ts",
+      "test_pdf_viewer_private_proxy.ts",
     ]
   }
 
+  if (enable_pdf_ink2 || enable_pdf_save_to_drive) {
+    files += [ "test_before_unload_proxy.ts" ]
+  }
+
   if (is_chromeos) {
     files += [ "printing_icon_test.ts" ]
   }
diff --git a/chrome/test/data/pdf/ink2_before_unload_stroke_test.ts b/chrome/test/data/pdf/ink2_before_unload_stroke_test.ts
index 66a7456..63a5b944 100644
--- a/chrome/test/data/pdf/ink2_before_unload_stroke_test.ts
+++ b/chrome/test/data/pdf/ink2_before_unload_stroke_test.ts
@@ -5,7 +5,8 @@
 import {AnnotationMode, PluginController} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {microtasksFinished} from 'chrome://webui-test/test_util.js';
 
-import {getNewTestBeforeUnloadProxy, setupTestMockPluginForInk, startFinishModifiedInkStroke} from './test_util.js';
+import {getNewTestBeforeUnloadProxy} from './test_before_unload_proxy.js';
+import {setupTestMockPluginForInk, startFinishModifiedInkStroke} from './test_util.js';
 
 chrome.test.runTests([
   // Test that the save unedited dialog appears when the user navigates away
diff --git a/chrome/test/data/pdf/ink2_before_unload_undo_test.ts b/chrome/test/data/pdf/ink2_before_unload_undo_test.ts
index 75ea11a..bbd5394 100644
--- a/chrome/test/data/pdf/ink2_before_unload_undo_test.ts
+++ b/chrome/test/data/pdf/ink2_before_unload_undo_test.ts
@@ -5,7 +5,8 @@
 import {AnnotationMode, PluginController} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {microtasksFinished} from 'chrome://webui-test/test_util.js';
 
-import {getNewTestBeforeUnloadProxy, setupTestMockPluginForInk, startFinishModifiedInkStroke} from './test_util.js';
+import {getNewTestBeforeUnloadProxy} from './test_before_unload_proxy.js';
+import {setupTestMockPluginForInk, startFinishModifiedInkStroke} from './test_util.js';
 
 chrome.test.runTests([
   // Test that the save unedited dialog does not appear when the user navigates
diff --git a/chrome/test/data/pdf/ink2_save_to_drive_cancelled_before_unload_test.ts b/chrome/test/data/pdf/ink2_save_to_drive_cancelled_before_unload_test.ts
new file mode 100644
index 0000000..15b3dd70
--- /dev/null
+++ b/chrome/test/data/pdf/ink2_save_to_drive_cancelled_before_unload_test.ts
@@ -0,0 +1,63 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {AnnotationMode, PluginController} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
+
+import {getNewTestBeforeUnloadProxy} from './test_before_unload_proxy.js';
+import {setUpTestPdfViewerPrivateProxy} from './test_pdf_viewer_private_proxy.js';
+import {getRequiredElement, setupTestMockPluginForInk, startFinishModifiedInkStroke} from './test_util.js';
+
+chrome.test.runTests([
+  // Test that the unedited dialog is shown after Save to Drive is canceled
+  // with an Ink2 stroke.
+  async function testSaveToDriveCanceledBeforeUnloadStroke() {
+    setupTestMockPluginForInk();
+
+    const viewer = document.body.querySelector('pdf-viewer')!;
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
+    const toolbar = viewer.$.toolbar;
+    const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
+    const controls =
+        getRequiredElement(viewer.$.toolbar, 'viewer-save-to-drive-controls');
+    const actionMenu = controls.$.menu;
+
+    chrome.test.assertEq(AnnotationMode.OFF, toolbar.annotationMode);
+
+    toolbar.setAnnotationMode(AnnotationMode.DRAW);
+    await microtasksFinished();
+    chrome.test.assertEq(AnnotationMode.DRAW, toolbar.annotationMode);
+
+    startFinishModifiedInkStroke(PluginController.getInstance());
+    await microtasksFinished();
+
+    // Click Save to Drive and the menu should open.
+    controls.$.save.click();
+    await eventToPromise('save-menu-shown-for-testing', controls);
+    chrome.test.assertTrue(actionMenu.open);
+
+    // Click on "Edited".
+    const buttons = actionMenu.querySelectorAll('button');
+    chrome.test.assertEq(2, buttons.length);
+    buttons[0]!.click();
+    await privateProxy.whenCalled('saveToDrive');
+    chrome.test.assertFalse(actionMenu.open);
+
+    // Mock that the upload is in progress.
+    privateProxy.sendUploadInProgress(25, 100);
+    await microtasksFinished();
+
+    // Click on "Cancel" in Save to Drive bubble.
+    const cancelButton = getRequiredElement(bubble, '#cancel-upload-button');
+    cancelButton.click();
+    await privateProxy.whenCalled('saveToDrive');
+    chrome.test.assertFalse(bubble.$.dialog.open);
+
+    const testProxy = getNewTestBeforeUnloadProxy();
+    window.location.href = 'about:blank';
+
+    chrome.test.assertEq(1, testProxy.getCallCount('preventDefault'));
+    chrome.test.succeed();
+  },
+]);
diff --git a/chrome/test/data/pdf/ink2_save_to_drive_success_before_unload_test.ts b/chrome/test/data/pdf/ink2_save_to_drive_success_before_unload_test.ts
new file mode 100644
index 0000000..fad3383
--- /dev/null
+++ b/chrome/test/data/pdf/ink2_save_to_drive_success_before_unload_test.ts
@@ -0,0 +1,58 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {AnnotationMode, PluginController} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
+
+import {getNewTestBeforeUnloadProxy} from './test_before_unload_proxy.js';
+import {setUpTestPdfViewerPrivateProxy} from './test_pdf_viewer_private_proxy.js';
+import {getRequiredElement, setupTestMockPluginForInk, startFinishModifiedInkStroke} from './test_util.js';
+
+chrome.test.runTests([
+  // Test that the unedited dialog is not shown after Save to Drive is completed
+  // with an Ink2 stroke.
+  async function testSaveToDriveCompletedBeforeUnloadStroke() {
+    setupTestMockPluginForInk();
+
+    const viewer = document.body.querySelector('pdf-viewer')!;
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
+    const toolbar = viewer.$.toolbar;
+    const controls =
+        getRequiredElement(viewer.$.toolbar, 'viewer-save-to-drive-controls');
+    const actionMenu = controls.$.menu;
+
+    chrome.test.assertEq(AnnotationMode.OFF, toolbar.annotationMode);
+
+    toolbar.setAnnotationMode(AnnotationMode.DRAW);
+    await microtasksFinished();
+    chrome.test.assertEq(AnnotationMode.DRAW, toolbar.annotationMode);
+
+    startFinishModifiedInkStroke(PluginController.getInstance());
+    await microtasksFinished();
+
+    // Click Save to Drive and the menu should open.
+    controls.$.save.click();
+    await eventToPromise('save-menu-shown-for-testing', controls);
+    chrome.test.assertTrue(actionMenu.open);
+
+    // Click on "Edited".
+    const buttons = actionMenu.querySelectorAll('button');
+    chrome.test.assertEq(2, buttons.length);
+    buttons[0]!.click();
+    await privateProxy.whenCalled('saveToDrive');
+    chrome.test.assertFalse(actionMenu.open);
+
+    // Mock that the upload is in progress and then completed.
+    privateProxy.sendUploadInProgress(25, 100);
+    await microtasksFinished();
+    privateProxy.sendUploadCompleted();
+    await microtasksFinished();
+
+    const testProxy = getNewTestBeforeUnloadProxy();
+    window.location.href = 'about:blank';
+
+    chrome.test.assertEq(0, testProxy.getCallCount('preventDefault'));
+    chrome.test.succeed();
+  },
+]);
diff --git a/chrome/test/data/pdf/ink2_save_to_drive_uploading_more_edits_before_unload_test.ts b/chrome/test/data/pdf/ink2_save_to_drive_uploading_more_edits_before_unload_test.ts
new file mode 100644
index 0000000..85f463c5
--- /dev/null
+++ b/chrome/test/data/pdf/ink2_save_to_drive_uploading_more_edits_before_unload_test.ts
@@ -0,0 +1,64 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {AnnotationMode, PluginController} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
+
+import {getNewTestBeforeUnloadProxy} from './test_before_unload_proxy.js';
+import {setUpTestPdfViewerPrivateProxy} from './test_pdf_viewer_private_proxy.js';
+import {getRequiredElement, setupTestMockPluginForInk, startFinishModifiedInkStroke} from './test_util.js';
+
+chrome.test.runTests([
+  // Test that the unedited dialog is shown if more edits are made while the
+  // upload is in progress.
+  async function testSaveToDriveUploadingMoreEditsBeforeUnloadStroke() {
+    setupTestMockPluginForInk();
+
+    const viewer = document.body.querySelector('pdf-viewer')!;
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
+    const toolbar = viewer.$.toolbar;
+    const controls =
+        getRequiredElement(viewer.$.toolbar, 'viewer-save-to-drive-controls');
+    const actionMenu = controls.$.menu;
+
+    chrome.test.assertEq(AnnotationMode.OFF, toolbar.annotationMode);
+
+    toolbar.setAnnotationMode(AnnotationMode.DRAW);
+    await microtasksFinished();
+    chrome.test.assertEq(AnnotationMode.DRAW, toolbar.annotationMode);
+
+    startFinishModifiedInkStroke(PluginController.getInstance());
+    await microtasksFinished();
+
+    // Click Save to Drive and the menu should open.
+    controls.$.save.click();
+    await eventToPromise('save-menu-shown-for-testing', controls);
+    chrome.test.assertTrue(actionMenu.open);
+
+    // Click on "Edited".
+    const buttons = actionMenu.querySelectorAll('button');
+    chrome.test.assertEq(2, buttons.length);
+    buttons[0]!.click();
+    await privateProxy.whenCalled('saveToDrive');
+    chrome.test.assertFalse(actionMenu.open);
+
+    // Mock that the upload is in progress.
+    privateProxy.sendUploadInProgress(25, 100);
+    await microtasksFinished();
+
+    // Make more edits.
+    startFinishModifiedInkStroke(PluginController.getInstance());
+    await microtasksFinished();
+
+    // Mock that the upload is completed.
+    privateProxy.sendUploadCompleted();
+    await microtasksFinished();
+
+    const testProxy = getNewTestBeforeUnloadProxy();
+    window.location.href = 'about:blank';
+
+    chrome.test.assertEq(1, testProxy.getCallCount('preventDefault'));
+    chrome.test.succeed();
+  },
+]);
diff --git a/chrome/test/data/pdf/save_to_drive_before_unload_uploading_test.ts b/chrome/test/data/pdf/save_to_drive_before_unload_uploading_test.ts
new file mode 100644
index 0000000..1416fe9
--- /dev/null
+++ b/chrome/test/data/pdf/save_to_drive_before_unload_uploading_test.ts
@@ -0,0 +1,28 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+
+import {microtasksFinished} from 'chrome://webui-test/test_util.js';
+
+import {getNewTestBeforeUnloadProxy} from './test_before_unload_proxy.js';
+import {setUpTestPdfViewerPrivateProxy} from './test_pdf_viewer_private_proxy.js';
+
+chrome.test.runTests([
+  // Test that the save unedited dialog appears when the user navigates away
+  // from the PDF when Save to Drive is uploading.
+  async function testSaveToDriveBeforePageLoaded() {
+    const viewer = document.body.querySelector('pdf-viewer')!;
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
+
+    privateProxy.sendUploadInProgress(25, 100);
+    await microtasksFinished();
+
+    const testProxy = getNewTestBeforeUnloadProxy();
+    window.location.href = 'about:blank';
+
+    chrome.test.assertEq(1, testProxy.getCallCount('preventDefault'));
+    chrome.test.succeed();
+  },
+]);
diff --git a/chrome/test/data/pdf/save_to_drive_test.ts b/chrome/test/data/pdf/save_to_drive_test.ts
index e9ffdfa..6e3aee87 100644
--- a/chrome/test/data/pdf/save_to_drive_test.ts
+++ b/chrome/test/data/pdf/save_to_drive_test.ts
@@ -4,13 +4,11 @@
 
 import 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 
-import type {PdfViewerPrivateProxy, ViewerSaveToDriveBubbleElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
-import {PdfViewerPrivateProxyImpl} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
-import {FakeChromeEvent} from 'chrome://webui-test/fake_chrome_event.js';
+import type {ViewerSaveToDriveBubbleElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {MockTimer} from 'chrome://webui-test/mock_timer.js';
-import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 import {microtasksFinished} from 'chrome://webui-test/test_util.js';
 
+import {setUpTestPdfViewerPrivateProxy} from './test_pdf_viewer_private_proxy.js';
 import {getRequiredElement} from './test_util.js';
 
 const SaveToDriveStatus = chrome.pdfViewerPrivate.SaveToDriveStatus;
@@ -18,81 +16,6 @@
 
 const viewer = document.body.querySelector('pdf-viewer')!;
 
-export class TestPdfViewerPrivateProxy extends TestBrowserProxy implements
-    PdfViewerPrivateProxy {
-  onSaveToDriveProgress: FakeChromeEvent;
-  private streamUrl_: string = '';
-
-  constructor() {
-    super([
-      'saveToDrive',
-      'setPdfDocumentTitle',
-    ]);
-
-    this.onSaveToDriveProgress = new FakeChromeEvent();
-  }
-
-  sendSaveToDriveProgress(
-      progress: chrome.pdfViewerPrivate.SaveToDriveProgress): void {
-    this.onSaveToDriveProgress.callListeners(this.streamUrl_, progress);
-  }
-
-  saveToDrive(saveRequestType?: chrome.pdfViewerPrivate.SaveRequestType): void {
-    this.methodCalled('saveToDrive', saveRequestType);
-  }
-
-  setPdfDocumentTitle(title: string): void {
-    this.methodCalled('setPdfDocumentTitle', title);
-  }
-
-  setStreamUrl(streamUrl: string): void {
-    this.streamUrl_ = streamUrl;
-  }
-
-  sendQuotaExceededError(): void {
-    this.sendSaveToDriveProgress({
-      status: SaveToDriveStatus.UPLOAD_IN_PROGRESS,
-      errorType: SaveToDriveErrorType.QUOTA_EXCEEDED,
-      accountEmail: 'test@gmail.com',
-    });
-  }
-
-  sendSessionTimeoutError(): void {
-    this.sendSaveToDriveProgress({
-      status: SaveToDriveStatus.UPLOAD_IN_PROGRESS,
-      errorType: SaveToDriveErrorType.OAUTH_ERROR,
-    });
-  }
-
-  sendUploadInProgress(uploadedBytes: number, fileSizeBytes: number): void {
-    this.sendSaveToDriveProgress({
-      status: SaveToDriveStatus.UPLOAD_IN_PROGRESS,
-      errorType: SaveToDriveErrorType.NO_ERROR,
-      uploadedBytes: uploadedBytes,
-      fileSizeBytes: fileSizeBytes,
-      fileMetadata: 'uploading, 2 minutes left',
-    });
-  }
-
-  sendUploadCompleted(): void {
-    this.sendSaveToDriveProgress({
-      status: SaveToDriveStatus.UPLOAD_COMPLETED,
-      errorType: SaveToDriveErrorType.NO_ERROR,
-      driveItemId: 'test-drive-item-id',
-      parentFolderName: 'test-parent-folder-name',
-      fileName: 'save_to_drive_test.pdf',
-      accountEmail: 'test@gmail.com',
-    });
-  }
-
-  sendUninitializedState(): void {
-    this.sendSaveToDriveProgress({
-      status: SaveToDriveStatus.NOT_STARTED,
-      errorType: SaveToDriveErrorType.NO_ERROR,
-    });
-  }
-}
-
 function assertBubbleAndProgressBar(
     bubble: ViewerSaveToDriveBubbleElement, value: number, max: number): void {
   chrome.test.assertTrue(bubble.$.dialog.open);
@@ -113,20 +36,12 @@
   chrome.test.assertFalse(bubble.$.dialog.open);
 }
 
-function setUpTestPrivateProxy(): TestPdfViewerPrivateProxy {
-  const privateProxy = new TestPdfViewerPrivateProxy();
-  privateProxy.setStreamUrl(viewer.getStreamUrlForTesting());
-  PdfViewerPrivateProxyImpl.setInstance(privateProxy);
-  viewer.setOnSaveToDriveProgressListenerForTesting();
-  return privateProxy;
-}
-
 // Unit tests for the pdf-viewer Save to Drive elements.
 const tests = [
   async function testSaveToDriveButton() {
     const controls =
         getRequiredElement(viewer.$.toolbar, 'viewer-save-to-drive-controls');
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
 
     chrome.test.assertEq('pdf:add-to-drive', controls.$.save.ironIcon);
 
@@ -154,7 +69,7 @@
   },
 
   async function testSaveToDriveBubble() {
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
     const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
 
     chrome.test.assertTrue(!!bubble);
@@ -211,7 +126,7 @@
   },
 
   async function testSaveToDriveBubbleCloseButtonAndStateResets() {
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
     const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
     const controls =
         getRequiredElement(viewer.$.toolbar, 'viewer-save-to-drive-controls');
@@ -240,7 +155,7 @@
   },
 
   async function testSaveToDriveBubbleCloseButtonNotResetting() {
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
     const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
     const controls =
         getRequiredElement(viewer.$.toolbar, 'viewer-save-to-drive-controls');
@@ -268,7 +183,7 @@
   },
 
   async function testSaveToDriveBubbleCancelUpload() {
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
     const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
 
     // Set the save to Drive state to uploading and open the bubble.
@@ -304,7 +219,7 @@
   },
 
   async function testSaveToDriveBubbleQuotaExceededAndManageStorageClick() {
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
     const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
 
     // Set quota exceeded state and open the bubble.
@@ -338,7 +253,7 @@
   },
 
   async function testSaveToDriveBubbleUploadCompletedAndOpenInDriveClick() {
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
     const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
 
     // Set upload completed state and open the bubble.
@@ -376,7 +291,7 @@
   },
 
   async function testSaveToDriveBubbleRetryUploadOriginal() {
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
     const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
 
     // Click on the save button to initiate an upload.
@@ -411,7 +326,7 @@
   },
 
   async function testSaveToDriveBubbleRetryUploadEdited() {
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
     const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
 
     // Click on the save button to initiate an edited upload.
@@ -437,21 +352,29 @@
     retryButton.click();
     await privateProxy.whenCalled('saveToDrive');
 
-    chrome.test.assertEq(2, privateProxy.getCallCount('saveToDrive'));
-    const args = privateProxy.getArgs('saveToDrive');
-    chrome.test.assertEq(2, args.length);
-    chrome.test.assertEq('EDITED', args[0]);
-    chrome.test.assertEq('EDITED', args[1]);
+    // Click on the save button again after `hasEdits` is false to reset the
+    // internal `saveToDriveRequestType_`, or else it will enable beforeunload
+    // dialog in the next test.
+    privateProxy.sendUninitializedState();
+    controls.hasEdits = false;
+    controls.$.save.click();
+    await privateProxy.whenCalled('saveToDrive');
 
     // Reset the bubble open state for the next test.
     closeBubble(bubble);
-    controls.hasEdits = false;
+
+    chrome.test.assertEq(3, privateProxy.getCallCount('saveToDrive'));
+    const args = privateProxy.getArgs('saveToDrive');
+    chrome.test.assertEq(3, args.length);
+    chrome.test.assertEq('EDITED', args[0]);
+    chrome.test.assertEq('EDITED', args[1]);
+    chrome.test.assertEq('ORIGINAL', args[2]);
 
     chrome.test.succeed();
   },
 
   async function testBubbleOpenAndCloseAutomaticallyAfterUploadInProgress() {
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
     const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
 
     privateProxy.sendUploadInProgress(0, 100);
@@ -483,7 +406,7 @@
   },
 
   async function testBubbleNotCloseAfterUploadInProgressIfOpenedManually() {
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
     const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
 
     privateProxy.sendUploadInProgress(0, 100);
@@ -516,7 +439,7 @@
   },
 
   async function testStateResetsAfterAccountChooserCanceled() {
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
     const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
 
     privateProxy.sendUploadInProgress(0, 100);
@@ -541,7 +464,7 @@
   },
 
   async function testSaveToDriveBubbleUploadInitialized() {
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
     const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
     const controls =
         getRequiredElement(viewer.$.toolbar, 'viewer-save-to-drive-controls');
@@ -559,14 +482,40 @@
         '566px', progress.$.innerProgress.getAttribute('stroke-dashoffset'));
     assertBubbleAndProgressBar(bubble, 0, 0);
 
-    // Reset the bubble open state for the next test.
+    // Reset the bubble and the upload for the next test.
+    privateProxy.sendUninitializedState();
+    await microtasksFinished();
     closeBubble(bubble);
 
     chrome.test.succeed();
   },
 
   async function testSaveToDriveBubbleConnectionError() {
-    const privateProxy = setUpTestPrivateProxy();
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
+    const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
+    const controls =
+        getRequiredElement(viewer.$.toolbar, 'viewer-save-to-drive-controls');
+
+    privateProxy.sendSaveToDriveProgress({
+      status: SaveToDriveStatus.UPLOAD_FAILED,
+      errorType: SaveToDriveErrorType.OFFLINE,
+    });
+    await microtasksFinished();
+    controls.$.save.click();
+    await microtasksFinished();
+
+    assertBubbleDescription(bubble, 'Check your internet connection');
+
+    // Reset the bubble and the upload for the next test.
+    privateProxy.sendUninitializedState();
+    await microtasksFinished();
+    closeBubble(bubble);
+
+    chrome.test.succeed();
+  },
+
+  async function testSaveToDriveBubbleConnectionError() {
+    const privateProxy = setUpTestPdfViewerPrivateProxy(viewer);
     const bubble = getRequiredElement(viewer, 'viewer-save-to-drive-bubble');
     const controls =
         getRequiredElement(viewer.$.toolbar, 'viewer-save-to-drive-controls');
diff --git a/chrome/test/data/pdf/test_before_unload_proxy.ts b/chrome/test/data/pdf/test_before_unload_proxy.ts
new file mode 100644
index 0000000..cb89a25
--- /dev/null
+++ b/chrome/test/data/pdf/test_before_unload_proxy.ts
@@ -0,0 +1,26 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+
+import type {BeforeUnloadProxy} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {BeforeUnloadProxyImpl} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+
+class TestBeforeUnloadProxy extends TestBrowserProxy implements
+    BeforeUnloadProxy {
+  constructor() {
+    super(['preventDefault']);
+  }
+
+  preventDefault() {
+    this.methodCalled('preventDefault');
+  }
+}
+
+export function getNewTestBeforeUnloadProxy(): TestBeforeUnloadProxy {
+  const testProxy = new TestBeforeUnloadProxy();
+  BeforeUnloadProxyImpl.setInstance(testProxy);
+  return testProxy;
+}
diff --git a/chrome/test/data/pdf/test_pdf_viewer_private_proxy.ts b/chrome/test/data/pdf/test_pdf_viewer_private_proxy.ts
new file mode 100644
index 0000000..bed6135
--- /dev/null
+++ b/chrome/test/data/pdf/test_pdf_viewer_private_proxy.ts
@@ -0,0 +1,97 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+
+import type {PdfViewerElement, PdfViewerPrivateProxy} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {PdfViewerPrivateProxyImpl} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {FakeChromeEvent} from 'chrome://webui-test/fake_chrome_event.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+
+const SaveToDriveStatus = chrome.pdfViewerPrivate.SaveToDriveStatus;
+const SaveToDriveErrorType = chrome.pdfViewerPrivate.SaveToDriveErrorType;
+
+class TestPdfViewerPrivateProxy extends TestBrowserProxy implements
+    PdfViewerPrivateProxy {
+  onSaveToDriveProgress: FakeChromeEvent;
+  private streamUrl_: string = '';
+
+  constructor() {
+    super([
+      'saveToDrive',
+      'setPdfDocumentTitle',
+    ]);
+
+    this.onSaveToDriveProgress = new FakeChromeEvent();
+  }
+
+  sendSaveToDriveProgress(
+      progress: chrome.pdfViewerPrivate.SaveToDriveProgress): void {
+    this.onSaveToDriveProgress.callListeners(this.streamUrl_, progress);
+  }
+
+  saveToDrive(saveRequestType?: chrome.pdfViewerPrivate.SaveRequestType): void {
+    this.methodCalled('saveToDrive', saveRequestType);
+  }
+
+  setPdfDocumentTitle(title: string): void {
+    this.methodCalled('setPdfDocumentTitle', title);
+  }
+
+  setStreamUrl(streamUrl: string): void {
+    this.streamUrl_ = streamUrl;
+  }
+
+  sendQuotaExceededError(): void {
+    this.sendSaveToDriveProgress({
+      status: SaveToDriveStatus.UPLOAD_IN_PROGRESS,
+      errorType: SaveToDriveErrorType.QUOTA_EXCEEDED,
+      accountEmail: 'test@gmail.com',
+    });
+  }
+
+  sendSessionTimeoutError(): void {
+    this.sendSaveToDriveProgress({
+      status: SaveToDriveStatus.UPLOAD_IN_PROGRESS,
+      errorType: SaveToDriveErrorType.OAUTH_ERROR,
+    });
+  }
+
+  sendUploadInProgress(uploadedBytes: number, fileSizeBytes: number): void {
+    this.sendSaveToDriveProgress({
+      status: SaveToDriveStatus.UPLOAD_IN_PROGRESS,
+      errorType: SaveToDriveErrorType.NO_ERROR,
+      uploadedBytes: uploadedBytes,
+      fileSizeBytes: fileSizeBytes,
+      fileMetadata: 'uploading, 2 minutes left',
+    });
+  }
+
+  sendUploadCompleted(): void {
+    this.sendSaveToDriveProgress({
+      status: SaveToDriveStatus.UPLOAD_COMPLETED,
+      errorType: SaveToDriveErrorType.NO_ERROR,
+      driveItemId: 'test-drive-item-id',
+      parentFolderName: 'test-parent-folder-name',
+      fileName: 'save_to_drive_test.pdf',
+      accountEmail: 'test@gmail.com',
+    });
+  }
+
+  sendUninitializedState(): void {
+    this.sendSaveToDriveProgress({
+      status: SaveToDriveStatus.NOT_STARTED,
+      errorType: SaveToDriveErrorType.NO_ERROR,
+    });
+  }
+}
+
+export function setUpTestPdfViewerPrivateProxy(viewer: PdfViewerElement):
+    TestPdfViewerPrivateProxy {
+  const privateProxy = new TestPdfViewerPrivateProxy();
+  privateProxy.setStreamUrl(viewer.getStreamUrlForTesting());
+  PdfViewerPrivateProxyImpl.setInstance(privateProxy);
+  viewer.setOnSaveToDriveProgressListenerForTesting();
+  return privateProxy;
+}
diff --git a/chrome/test/data/pdf/test_util.ts b/chrome/test/data/pdf/test_util.ts
index 6dac7fb..f22ae79 100644
--- a/chrome/test/data/pdf/test_util.ts
+++ b/chrome/test/data/pdf/test_util.ts
@@ -8,13 +8,10 @@
 import type {Bookmark, DocumentDimensions, LayoutOptions, PdfViewerElement, ViewerToolbarElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {resetForTesting as resetMetricsForTesting, UserAction, Viewport} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 // <if expr="enable_pdf_ink2">
-import type {AnnotationBrush, BeforeUnloadProxy, InkBrushSelectorElement, InkColorSelectorElement, InkSizeSelectorElement, SelectableIconButtonElement, ViewerBottomToolbarDropdownElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
-import {AnnotationBrushType, BeforeUnloadProxyImpl, DEFAULT_TEXTBOX_WIDTH, MIN_TEXTBOX_SIZE_PX, hexToColor, Ink2Manager, TEXT_COLORS, TextAlignment, TextStyle, PluginController, PluginControllerEventType} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import type {AnnotationBrush, InkBrushSelectorElement, InkColorSelectorElement, InkSizeSelectorElement, SelectableIconButtonElement, ViewerBottomToolbarDropdownElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {AnnotationBrushType, DEFAULT_TEXTBOX_WIDTH, MIN_TEXTBOX_SIZE_PX, hexToColor, Ink2Manager, TEXT_COLORS, TextAlignment, TextStyle, PluginController, PluginControllerEventType} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 // </if>
 import {CrLitElement, html} from 'chrome://resources/lit/v3_0/lit.rollup.js';
-// <if expr="enable_pdf_ink2">
-import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-// </if>
 import {eventToPromise, isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
 // clang-format on
 
@@ -532,23 +529,6 @@
       PluginControllerEventType.PLUGIN_MESSAGE, {detail: message}));
 }
 
-export class TestBeforeUnloadProxy extends TestBrowserProxy implements
-    BeforeUnloadProxy {
-  constructor() {
-    super(['preventDefault']);
-  }
-
-  preventDefault() {
-    this.methodCalled('preventDefault');
-  }
-}
-
-export function getNewTestBeforeUnloadProxy(): TestBeforeUnloadProxy {
-  const testProxy = new TestBeforeUnloadProxy();
-  BeforeUnloadProxyImpl.setInstance(testProxy);
-  return testProxy;
-}
-
 export function setupTestMockPluginForInk(): MockPdfPluginElement {
   const controller = PluginController.getInstance();
   const mockPlugin = createMockPdfPluginForTest();
diff --git a/chrome/test/data/webui/glic/browser_tests/glic_api_browsertest.ts b/chrome/test/data/webui/glic/browser_tests/glic_api_browsertest.ts
index 18fa493..918f148 100644
--- a/chrome/test/data/webui/glic/browser_tests/glic_api_browsertest.ts
+++ b/chrome/test/data/webui/glic/browser_tests/glic_api_browsertest.ts
@@ -1648,6 +1648,42 @@
   }
 
   /**
+   * Verifies that getPageMetadata emits new values when the tab navigates to a
+   * new page.
+   */
+  async testGetPageMetadataOnNavigation() {
+    assertDefined(this.host.getPageMetadata);
+    assertDefined(this.host.getFocusedTabStateV2);
+
+    const focus =
+        await observeSequence(this.host.getFocusedTabStateV2()).next();
+    const tabId = checkDefined(focus.hasFocus?.tabData.tabId);
+
+    const metadataObservable =
+        this.host.getPageMetadata(tabId, ['author', 'description']);
+    assertDefined(metadataObservable);
+    const metadataSequence = observeSequence(metadataObservable);
+
+    // The initial page has one meta tag.
+    let metadata: PageMetadata = await metadataSequence.next();
+    assertDefined(metadata);
+    assertEquals(1, metadata.frameMetadata.length);
+    assertEquals(1, metadata.frameMetadata[0]!.metaTags.length);
+    const authorTag =
+        metadata.frameMetadata[0]!.metaTags.find(tag => tag.name === 'author');
+    assertDefined(authorTag);
+    assertEquals('George', authorTag.content);
+
+    // The C++ side will navigate to a page with no meta tags.
+    await this.advanceToNextStep();
+
+    metadata = await metadataSequence.next();
+    assertDefined(metadata);
+    assertEquals(1, metadata.frameMetadata.length);
+    assertEquals(0, metadata.frameMetadata[0]!.metaTags.length);
+  }
+
+  /**
    * Checks that the `ObservableValue` stops emitting updates after the
    * associated tab is closed.
    */
diff --git a/chrome/test/data/webui/new_tab_footer/app_test.ts b/chrome/test/data/webui/new_tab_footer/app_test.ts
index 61d5743..ac28b33 100644
--- a/chrome/test/data/webui/new_tab_footer/app_test.ts
+++ b/chrome/test/data/webui/new_tab_footer/app_test.ts
@@ -6,8 +6,9 @@
 import {CustomizeDialogPage, FooterCustomizeChromeEntryPoint, FooterElement} from 'chrome://newtab-footer/app.js';
 import {NewTabFooterDocumentProxy} from 'chrome://newtab-footer/browser_proxy.js';
 import type {CustomizeButtonsDocumentRemote} from 'chrome://newtab-footer/customize_buttons.mojom-webui.js';
-import {CustomizeButtonsDocumentCallbackRouter, CustomizeButtonsHandlerRemote, CustomizeChromeSection, SidePanelOpenTrigger} from 'chrome://newtab-footer/customize_buttons.mojom-webui.js';
+import {CustomizeButtonsDocumentCallbackRouter, CustomizeButtonsHandlerRemote, SidePanelOpenTrigger} from 'chrome://newtab-footer/customize_buttons.mojom-webui.js';
 import {CustomizeButtonsProxy} from 'chrome://newtab-footer/customize_buttons_proxy.js';
+import {CustomizeChromeSection} from 'chrome://newtab-footer/customize_chrome.mojom-webui.js';
 import type {BackgroundAttribution, ManagementNotice, NewTabFooterDocumentRemote} from 'chrome://newtab-footer/new_tab_footer.mojom-webui.js';
 import {NewTabFooterDocumentCallbackRouter, NewTabFooterHandlerRemote, NewTabPageType} from 'chrome://newtab-footer/new_tab_footer.mojom-webui.js';
 import {WindowProxy} from 'chrome://newtab-footer/window_proxy.js';
diff --git a/chrome/test/data/webui/new_tab_page/app_test.ts b/chrome/test/data/webui/new_tab_page/app_test.ts
index d37293e0..50ac7aa 100644
--- a/chrome/test/data/webui/new_tab_page/app_test.ts
+++ b/chrome/test/data/webui/new_tab_page/app_test.ts
@@ -3,7 +3,8 @@
 // found in the LICENSE file.
 
 import type {CustomizeButtonsDocumentRemote} from 'chrome://new-tab-page/customize_buttons.mojom-webui.js';
-import {CustomizeButtonsDocumentCallbackRouter, CustomizeButtonsHandlerRemote, CustomizeChromeSection, SidePanelOpenTrigger} from 'chrome://new-tab-page/customize_buttons.mojom-webui.js';
+import {CustomizeButtonsDocumentCallbackRouter, CustomizeButtonsHandlerRemote, SidePanelOpenTrigger} from 'chrome://new-tab-page/customize_buttons.mojom-webui.js';
+import {CustomizeChromeSection} from 'chrome://new-tab-page/customize_chrome.mojom-webui.js';
 import type {Module} from 'chrome://new-tab-page/lazy_load.js';
 import {ComposeboxProxyImpl, counterfactualLoad, ModuleDescriptor, ModuleRegistry} from 'chrome://new-tab-page/lazy_load.js';
 import {$$, BackgroundManager, BrowserCommandProxy, CUSTOMIZE_CHROME_BUTTON_ELEMENT_ID, CustomizeButtonsProxy, CustomizeDialogPage, NewTabPageProxy, NtpCustomizeChromeEntryPoint, NtpElement, VoiceAction, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
diff --git a/chrome/test/data/webui/optimization_guide_internals/optimization_guide_internals_browsertest.cc b/chrome/test/data/webui/optimization_guide_internals/optimization_guide_internals_browsertest.cc
index 7ffd19b2..c2f2d4b 100644
--- a/chrome/test/data/webui/optimization_guide_internals/optimization_guide_internals_browsertest.cc
+++ b/chrome/test/data/webui/optimization_guide_internals/optimization_guide_internals_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
+#include "base/task/thread_pool.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/browser_process.h"
@@ -158,9 +159,12 @@
             }));
 
     OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
-        ->AddObserverForOptimizationTargetModel(optimization_target,
-                                                /*model_metadata=*/std::nullopt,
-                                                &model_file_observer);
+        ->AddObserverForOptimizationTargetModel(
+            optimization_target,
+            /*model_metadata=*/std::nullopt,
+            base::ThreadPool::CreateSequencedTaskRunner(
+                {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
+            &model_file_observer);
 
     run_loop.Run();
   }
diff --git a/chrome/test/data/webui/settings/about_page_test.ts b/chrome/test/data/webui/settings/about_page_test.ts
index f014eb94..9b400046 100644
--- a/chrome/test/data/webui/settings/about_page_test.ts
+++ b/chrome/test/data/webui/settings/about_page_test.ts
@@ -3,9 +3,8 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import type {SettingsAboutPageElement} from 'chrome://settings/settings.js';
-import {AboutPageBrowserProxyImpl, LifetimeBrowserProxyImpl, Router, routes} from 'chrome://settings/settings.js';
+import {AboutPageBrowserProxyImpl, LifetimeBrowserProxyImpl, loadTimeData, Router, routes} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {TestAboutPageBrowserProxy} from './test_about_page_browser_proxy.js';
diff --git a/chrome/test/data/webui/settings/all_sites_test.ts b/chrome/test/data/webui/settings/all_sites_test.ts
index 3a69dfd..79e70ad 100644
--- a/chrome/test/data/webui/settings/all_sites_test.ts
+++ b/chrome/test/data/webui/settings/all_sites_test.ts
@@ -3,11 +3,10 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {AllSitesElement, SiteGroup} from 'chrome://settings/lazy_load.js';
 import {ContentSetting, ContentSettingsTypes, SiteSettingsPrefsBrowserProxyImpl, SortMethod} from 'chrome://settings/lazy_load.js';
-import {CrSettingsPrefs, DeleteBrowsingDataAction, MetricsBrowserProxyImpl, Router, routes} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, DeleteBrowsingDataAction, loadTimeData, MetricsBrowserProxyImpl, Router, routes} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/settings/appearance_page_test.ts b/chrome/test/data/webui/settings/appearance_page_test.ts
index e210875..af1dd4a 100644
--- a/chrome/test/data/webui/settings/appearance_page_test.ts
+++ b/chrome/test/data/webui/settings/appearance_page_test.ts
@@ -2,10 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {CustomizeColorSchemeModeClientRemote, SettingsAppearancePageElement, SettingsDropdownMenuElement, SettingsToggleButtonElement} from 'chrome://settings/settings.js';
-import {AppearanceBrowserProxyImpl, ColorSchemeMode, CustomizeColorSchemeModeBrowserProxy, CustomizeColorSchemeModeClientCallbackRouter, CustomizeColorSchemeModeHandlerRemote, SystemTheme} from 'chrome://settings/settings.js';
+import {AppearanceBrowserProxyImpl, ColorSchemeMode, CustomizeColorSchemeModeBrowserProxy, CustomizeColorSchemeModeClientCallbackRouter, CustomizeColorSchemeModeHandlerRemote, loadTimeData, SystemTheme} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 import {isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/settings/autofill_page_test.ts b/chrome/test/data/webui/settings/autofill_page_test.ts
index 6e1f825..74bef672 100644
--- a/chrome/test/data/webui/settings/autofill_page_test.ts
+++ b/chrome/test/data/webui/settings/autofill_page_test.ts
@@ -6,13 +6,12 @@
 import 'chrome://settings/lazy_load.js';
 
 // clang-format off
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import type {SettingsAutofillSectionElement, SettingsPaymentsSectionElement} from 'chrome://settings/lazy_load.js';
 import {AutofillManagerImpl, PaymentsManagerImpl} from 'chrome://settings/lazy_load.js';
 import type {CrLinkRowElement, SettingsAutofillPageElement, SettingsPrefsElement} from 'chrome://settings/settings.js';
-import {AutofillSettingsReferrer, CrSettingsPrefs, MetricsBrowserProxyImpl, OpenWindowProxyImpl, PasswordManagerImpl, PasswordManagerPage, resetRouterForTesting, SettingsPluralStringProxyImpl} from 'chrome://settings/settings.js';
+import {AutofillSettingsReferrer, CrSettingsPrefs, loadTimeData, MetricsBrowserProxyImpl, OpenWindowProxyImpl, PasswordManagerImpl, PasswordManagerPage, resetRouterForTesting, SettingsPluralStringProxyImpl} from 'chrome://settings/settings.js';
 import {assertEquals, assertDeepEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {FakeSettingsPrivate} from 'chrome://webui-test/fake_settings_private.js';
 import {TestPluralStringProxy} from 'chrome://webui-test/test_plural_string_proxy.js';
diff --git a/chrome/test/data/webui/settings/autofill_section_test.ts b/chrome/test/data/webui/settings/autofill_section_test.ts
index 054bbbe..1595467b 100644
--- a/chrome/test/data/webui/settings/autofill_section_test.ts
+++ b/chrome/test/data/webui/settings/autofill_section_test.ts
@@ -5,7 +5,6 @@
 // clang-format off
 import 'chrome://settings/settings.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {CrInputElement, CrTextareaElement} from 'chrome://settings/lazy_load.js';
 import {AutofillAddressOptInChange, AutofillManagerImpl, CountryDetailManagerProxyImpl} from 'chrome://settings/lazy_load.js';
@@ -13,7 +12,7 @@
 import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
 import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import type {CrLinkRowElement} from 'chrome://settings/settings.js';
-import {OpenWindowProxyImpl} from 'chrome://settings/settings.js';
+import {loadTimeData, OpenWindowProxyImpl} from 'chrome://settings/settings.js';
 import {eventToPromise, whenAttributeIs, isVisible} from 'chrome://webui-test/test_util.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestOpenWindowProxy} from 'chrome://webui-test/test_open_window_proxy.js';
diff --git a/chrome/test/data/webui/settings/chooser_exception_list_test.ts b/chrome/test/data/webui/settings/chooser_exception_list_test.ts
index 3369421..b2157ad 100644
--- a/chrome/test/data/webui/settings/chooser_exception_list_test.ts
+++ b/chrome/test/data/webui/settings/chooser_exception_list_test.ts
@@ -7,8 +7,8 @@
 import 'chrome://webui-test/cr_elements/cr_policy_strings.js';
 
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {loadTimeData} from 'chrome://settings/settings.js';
 import type {ChooserException, ChooserExceptionListElement, RawChooserException, RawSiteException, SiteException} from 'chrome://settings/lazy_load.js';
 import {ChooserType, ContentSettingsTypes, SiteSettingSource, SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/settings/clear_browsing_data_dialog_v2_test.ts b/chrome/test/data/webui/settings/clear_browsing_data_dialog_v2_test.ts
index 3724ded3ac..2490798 100644
--- a/chrome/test/data/webui/settings/clear_browsing_data_dialog_v2_test.ts
+++ b/chrome/test/data/webui/settings/clear_browsing_data_dialog_v2_test.ts
@@ -5,12 +5,11 @@
 // clang-format off
 
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import type {ClearBrowsingDataResult, SettingsCheckboxElement, SettingsClearBrowsingDataDialogV2Element, SettingsHistoryDeletionDialogElement} from 'chrome://settings/lazy_load.js';
 import {BrowsingDataType, ClearBrowsingDataBrowserProxyImpl, getDataTypePrefName, getTimePeriodString, TimePeriod} from 'chrome://settings/lazy_load.js';
 import type {SettingsPrefsElement} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs, MetricsBrowserProxyImpl, SignedInState, StatusAction, SyncBrowserProxyImpl, Router, routes, resetRouterForTesting} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData, MetricsBrowserProxyImpl, SignedInState, StatusAction, SyncBrowserProxyImpl, Router, routes, resetRouterForTesting} from 'chrome://settings/settings.js';
 import {assertArrayEquals, assertEquals, assertFalse, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/settings/cookies_page_test.ts b/chrome/test/data/webui/settings/cookies_page_test.ts
index c0c1533..d2cd6ca 100644
--- a/chrome/test/data/webui/settings/cookies_page_test.ts
+++ b/chrome/test/data/webui/settings/cookies_page_test.ts
@@ -3,12 +3,11 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {SettingsCollapseRadioButtonElement, SettingsRadioGroupElement, SettingsCookiesPageElement} from 'chrome://settings/lazy_load.js';
 import {ContentSettingsTypes, SITE_EXCEPTION_WILDCARD, SiteSettingsPrefsBrowserProxyImpl,ThirdPartyCookieBlockingSetting} from 'chrome://settings/lazy_load.js';
 import type {SettingsPrefsElement, SettingsToggleButtonElement} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs, MetricsBrowserProxyImpl, PrivacyElementInteractions, resetRouterForTesting, Router} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData, MetricsBrowserProxyImpl, PrivacyElementInteractions, resetRouterForTesting, Router} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, isChildVisible} from 'chrome://webui-test/test_util.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/settings/default_browser_test.ts b/chrome/test/data/webui/settings/default_browser_test.ts
index 5dee5ec..acffc73a 100644
--- a/chrome/test/data/webui/settings/default_browser_test.ts
+++ b/chrome/test/data/webui/settings/default_browser_test.ts
@@ -5,10 +5,9 @@
 // clang-format off
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {DefaultBrowserBrowserProxy, DefaultBrowserInfo, SettingsDefaultBrowserPageElement} from 'chrome://settings/settings.js';
-import {DefaultBrowserBrowserProxyImpl} from 'chrome://settings/settings.js';
+import {DefaultBrowserBrowserProxyImpl, loadTimeData} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 // clang-format on
 
 /**
diff --git a/chrome/test/data/webui/settings/do_not_track_toggle_test.ts b/chrome/test/data/webui/settings/do_not_track_toggle_test.ts
index a0270c6..803c8d7 100644
--- a/chrome/test/data/webui/settings/do_not_track_toggle_test.ts
+++ b/chrome/test/data/webui/settings/do_not_track_toggle_test.ts
@@ -5,11 +5,10 @@
 // clang-format off
 import 'chrome://settings/lazy_load.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {SettingsDoNotTrackToggleElement} from 'chrome://settings/lazy_load.js';
 import type {SettingsToggleButtonElement} from 'chrome://settings/settings.js';
-import {MetricsBrowserProxyImpl, PrivacyElementInteractions} from 'chrome://settings/settings.js';
+import {loadTimeData, MetricsBrowserProxyImpl, PrivacyElementInteractions} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
diff --git a/chrome/test/data/webui/settings/downloads_page_test.ts b/chrome/test/data/webui/settings/downloads_page_test.ts
index ab87082..84abdaa 100644
--- a/chrome/test/data/webui/settings/downloads_page_test.ts
+++ b/chrome/test/data/webui/settings/downloads_page_test.ts
@@ -5,7 +5,6 @@
 // clang-format off
 import 'chrome://settings/settings.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
@@ -13,7 +12,7 @@
 import {DownloadsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import type {SettingsPrefsElement} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData} from 'chrome://settings/settings.js';
 // <if expr="is_chromeos">
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 // </if>
diff --git a/chrome/test/data/webui/settings/file_system_site_details_test.ts b/chrome/test/data/webui/settings/file_system_site_details_test.ts
index 7670758..fe630531 100644
--- a/chrome/test/data/webui/settings/file_system_site_details_test.ts
+++ b/chrome/test/data/webui/settings/file_system_site_details_test.ts
@@ -7,10 +7,9 @@
 
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import type {FileSystemGrant, OriginFileSystemGrants} from 'chrome://settings/lazy_load.js';
 import {FileSystemSiteDetailsElement, SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
-import {CrSettingsPrefs, Router, routes} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData, Router, routes} from 'chrome://settings/settings.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 import {TestSiteSettingsPrefsBrowserProxy} from './test_site_settings_prefs_browser_proxy.js';
diff --git a/chrome/test/data/webui/settings/file_system_site_entry_item_test.ts b/chrome/test/data/webui/settings/file_system_site_entry_item_test.ts
index 746e7bcb..8e52bf7 100644
--- a/chrome/test/data/webui/settings/file_system_site_entry_item_test.ts
+++ b/chrome/test/data/webui/settings/file_system_site_entry_item_test.ts
@@ -5,10 +5,9 @@
 // clang-format off
 import 'chrome://settings/lazy_load.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {FileSystemGrant, FileSystemSiteEntryItemElement} from 'chrome://settings/lazy_load.js';
-import {CrSettingsPrefs} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData} from 'chrome://settings/settings.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/settings/file_system_site_entry_test.ts b/chrome/test/data/webui/settings/file_system_site_entry_test.ts
index bde2272..bebd52cc 100644
--- a/chrome/test/data/webui/settings/file_system_site_entry_test.ts
+++ b/chrome/test/data/webui/settings/file_system_site_entry_test.ts
@@ -5,11 +5,10 @@
 // clang-format off
 import 'chrome://settings/lazy_load.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {FileSystemGrant, OriginFileSystemGrants} from 'chrome://settings/lazy_load.js';
 import {FileSystemSiteEntryElement, SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
-import {CrSettingsPrefs, Router, routes} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData, Router, routes} from 'chrome://settings/settings.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/settings/geolocation_page_test.ts b/chrome/test/data/webui/settings/geolocation_page_test.ts
index 0fa77b8..d62691c 100644
--- a/chrome/test/data/webui/settings/geolocation_page_test.ts
+++ b/chrome/test/data/webui/settings/geolocation_page_test.ts
@@ -3,11 +3,10 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import type {GeolocationPageElement} from 'chrome://settings/lazy_load.js';
 import {ContentSetting, ContentSettingsTypes, SiteSettingsPrefsBrowserProxyImpl, SettingsState} from 'chrome://settings/lazy_load.js';
 import type {SettingsPrefsElement} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/settings/incognito_tracking_protections_page_test.ts b/chrome/test/data/webui/settings/incognito_tracking_protections_page_test.ts
index 2f2408a9..74159d5 100644
--- a/chrome/test/data/webui/settings/incognito_tracking_protections_page_test.ts
+++ b/chrome/test/data/webui/settings/incognito_tracking_protections_page_test.ts
@@ -2,11 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {IncognitoTrackingProtectionsPageElement} from 'chrome://settings/lazy_load.js';
 import type {SettingsPrefsElement, SettingsToggleButtonElement} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs, Router, resetRouterForTesting,MetricsBrowserProxyImpl, PrivacyElementInteractions} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData, MetricsBrowserProxyImpl, PrivacyElementInteractions, resetRouterForTesting, Router} from 'chrome://settings/settings.js';
 import {assertTrue, assertFalse, assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible} from 'chrome://webui-test/test_util.js';
 import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
diff --git a/chrome/test/data/webui/settings/languages_page_metrics_test_browser.ts b/chrome/test/data/webui/settings/languages_page_metrics_test_browser.ts
index 3e88d8d..c5da5aa 100644
--- a/chrome/test/data/webui/settings/languages_page_metrics_test_browser.ts
+++ b/chrome/test/data/webui/settings/languages_page_metrics_test_browser.ts
@@ -2,11 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {LanguageHelper, SettingsLanguagesPageElement} from 'chrome://settings/lazy_load.js';
 import {LanguagesBrowserProxyImpl, LanguageSettingsMetricsProxyImpl, LanguageSettingsPageImpressionType} from 'chrome://settings/lazy_load.js';
-import {CrSettingsPrefs} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData} from 'chrome://settings/settings.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {FakeSettingsPrivate} from 'chrome://webui-test/fake_settings_private.js';
 import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/settings/notifications_page_test.ts b/chrome/test/data/webui/settings/notifications_page_test.ts
index ff9bd89..4da8277 100644
--- a/chrome/test/data/webui/settings/notifications_page_test.ts
+++ b/chrome/test/data/webui/settings/notifications_page_test.ts
@@ -4,11 +4,10 @@
 
 // clang-format off
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import type {NotificationsPageElement} from 'chrome://settings/lazy_load.js';
 import {ContentSetting, ContentSettingsTypes, SiteSettingsPrefsBrowserProxyImpl, SettingsState, SafetyHubBrowserProxyImpl, SafetyHubEvent} from 'chrome://settings/lazy_load.js';
 import type {SettingsPrefsElement} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs, resetRouterForTesting, resetPageVisibilityForTesting} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData, resetRouterForTesting, resetPageVisibilityForTesting} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/settings/people_page_sync_page_test.ts b/chrome/test/data/webui/settings/people_page_sync_page_test.ts
index 6636d320..db0272c7 100644
--- a/chrome/test/data/webui/settings/people_page_sync_page_test.ts
+++ b/chrome/test/data/webui/settings/people_page_sync_page_test.ts
@@ -6,7 +6,6 @@
 import 'chrome://settings/lazy_load.js';
 
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {CrExpandButtonElement, CrInputElement, SettingsSyncEncryptionOptionsElement, SettingsSyncPageElement} from 'chrome://settings/lazy_load.js';
 // <if expr="not is_chromeos">
@@ -15,7 +14,7 @@
 import type {CrCollapseElement} from 'chrome://settings/lazy_load.js';
 import type {CrButtonElement, CrRadioButtonElement, CrRadioGroupElement} from 'chrome://settings/settings.js';
 import {MetricsBrowserProxyImpl} from 'chrome://settings/settings.js';
-import {OpenWindowProxyImpl, PageStatus, Router, routes, SignedInState, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
+import {loadTimeData, OpenWindowProxyImpl, PageStatus, Router, routes, SignedInState, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {TestOpenWindowProxy} from 'chrome://webui-test/test_open_window_proxy.js';
diff --git a/chrome/test/data/webui/settings/privacy_guide_integration_test.ts b/chrome/test/data/webui/settings/privacy_guide_integration_test.ts
index 91c0d10..1e45cec 100644
--- a/chrome/test/data/webui/settings/privacy_guide_integration_test.ts
+++ b/chrome/test/data/webui/settings/privacy_guide_integration_test.ts
@@ -5,11 +5,10 @@
 // clang-format off
 import 'chrome://settings/settings.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import type {SettingsPrivacyGuidePageElement} from 'chrome://settings/lazy_load.js';
 import {PrivacyGuideStep} from 'chrome://settings/lazy_load.js';
 import type {SettingsPrefsElement} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs, MetricsBrowserProxyImpl, PrivacyGuideStepsEligibleAndReached, Router, routes, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData, MetricsBrowserProxyImpl, PrivacyGuideStepsEligibleAndReached, Router, routes, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {assertTrue, assertNotReached} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/settings/privacy_guide_page_test.ts b/chrome/test/data/webui/settings/privacy_guide_page_test.ts
index e4ea8f5..4770231 100644
--- a/chrome/test/data/webui/settings/privacy_guide_page_test.ts
+++ b/chrome/test/data/webui/settings/privacy_guide_page_test.ts
@@ -4,12 +4,11 @@
 
 // clang-format off
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {SettingsPrivacyGuideDialogElement, SettingsPrivacyGuidePageElement} from 'chrome://settings/lazy_load.js';
 import {ContentSetting, CookieControlsMode, PrivacyGuideStep, SafeBrowsingSetting, ThirdPartyCookieBlockingSetting} from 'chrome://settings/lazy_load.js';
 import type {SettingsPrefsElement, SyncStatus} from 'chrome://settings/settings.js';
-import {HatsBrowserProxyImpl, TrustSafetyInteraction, CrSettingsPrefs, MetricsBrowserProxyImpl, PrivacyGuideStepsEligibleAndReached, PrivacyGuideBrowserProxyImpl, PrivacyGuideInteractions, resetRouterForTesting, Router, routes, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, HatsBrowserProxyImpl, loadTimeData, MetricsBrowserProxyImpl, PrivacyGuideBrowserProxyImpl, PrivacyGuideInteractions, PrivacyGuideStepsEligibleAndReached, resetRouterForTesting, Router, routes, StatusAction, SyncBrowserProxyImpl, TrustSafetyInteraction} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/settings/privacy_guide_safe_browsing_fragment_test.ts b/chrome/test/data/webui/settings/privacy_guide_safe_browsing_fragment_test.ts
index 95cba1a8..7c5dc88 100644
--- a/chrome/test/data/webui/settings/privacy_guide_safe_browsing_fragment_test.ts
+++ b/chrome/test/data/webui/settings/privacy_guide_safe_browsing_fragment_test.ts
@@ -3,12 +3,11 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {PrivacyGuideSafeBrowsingFragmentElement, SettingsCollapseRadioButtonElement, SettingsRadioGroupElement} from 'chrome://settings/lazy_load.js';
 import {SafeBrowsingSetting} from 'chrome://settings/lazy_load.js';
 import type {SettingsPrefsElement} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs, MetricsBrowserProxyImpl, PrivacyGuideSettingsStates, resetRouterForTesting} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData, MetricsBrowserProxyImpl, PrivacyGuideSettingsStates, resetRouterForTesting} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, isChildVisible} from 'chrome://webui-test/test_util.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/settings/privacy_guide_test_util.ts b/chrome/test/data/webui/settings/privacy_guide_test_util.ts
index 15f35e3..6f6c0dd 100644
--- a/chrome/test/data/webui/settings/privacy_guide_test_util.ts
+++ b/chrome/test/data/webui/settings/privacy_guide_test_util.ts
@@ -4,11 +4,10 @@
 
 // clang-format off
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import type {SettingsPrivacyGuidePageElement, ThirdPartyCookieBlockingSetting} from 'chrome://settings/lazy_load.js';
 import {ContentSetting, CookieControlsMode, PrivacyGuideStep, SafeBrowsingSetting} from 'chrome://settings/lazy_load.js';
 import type {SettingsPrefsElement} from 'chrome://settings/settings.js';
-import {Router, routes, SignedInState, StatusAction} from 'chrome://settings/settings.js';
+import {loadTimeData, Router, routes, SignedInState, StatusAction} from 'chrome://settings/settings.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible} from 'chrome://webui-test/test_util.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/settings/privacy_page_test.ts b/chrome/test/data/webui/settings/privacy_page_test.ts
index 6cc587b..6c5220c 100644
--- a/chrome/test/data/webui/settings/privacy_page_test.ts
+++ b/chrome/test/data/webui/settings/privacy_page_test.ts
@@ -4,12 +4,11 @@
 
 // clang-format off
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {CrToastElement} from 'chrome://settings/lazy_load.js';
 import {ClearBrowsingDataBrowserProxyImpl, CookieControlsMode} from 'chrome://settings/lazy_load.js';
 import type {CrLinkRowElement, Route, SettingsPrefsElement, SettingsPrivacyPageElement, SyncStatus} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs, HatsBrowserProxyImpl, MetricsBrowserProxyImpl, PrivacyGuideInteractions, PrivacyPageBrowserProxyImpl, resetRouterForTesting, Router, routes, StatusAction, TrustSafetyInteraction} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, HatsBrowserProxyImpl, loadTimeData, MetricsBrowserProxyImpl, PrivacyGuideInteractions, PrivacyPageBrowserProxyImpl, resetRouterForTesting, Router, routes, StatusAction, TrustSafetyInteraction} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue, assertThrows} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, isChildVisible} from 'chrome://webui-test/test_util.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
@@ -756,8 +755,8 @@
     assertEquals(TrustSafetyInteraction.USED_PRIVACY_CARD, interaction);
   });
 
-  test('PermissionsTrigger', async function() {
-    page.$.permissionsLinkRow.click();
+  test('SiteSettingsTrigger', async function() {
+    page.$.siteSettingsLinkRow.click();
     const interaction =
         await testHatsBrowserProxy.whenCalled('trustSafetyInteractionOccurred');
     assertEquals(TrustSafetyInteraction.USED_PRIVACY_CARD, interaction);
diff --git a/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts b/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts
index 1719685..d239456 100644
--- a/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts
+++ b/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts
@@ -5,11 +5,10 @@
 import 'chrome://settings/lazy_load.js';
 
 import {assert} from 'chrome://resources/js/assert.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import type {CrExpandButtonElement, SettingsPrivacySandboxAdMeasurementSubpageElement, SettingsPrivacySandboxManageTopicsSubpageElement, SettingsPrivacySandboxPageElement, SettingsPrivacySandboxTopicsSubpageElement, SettingsSimpleConfirmationDialogElement} from 'chrome://settings/lazy_load.js';
 import {SettingsPrivacySandboxFledgeSubpageElement} from 'chrome://settings/lazy_load.js';
 import type {CrButtonElement, CrLinkRowElement, FirstLevelTopicsState, SettingsPrefsElement, SettingsToggleButtonElement, TopicsState} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs, MetricsBrowserProxyImpl, PrivacySandboxBrowserProxyImpl, Router, routes} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData, MetricsBrowserProxyImpl, PrivacySandboxBrowserProxyImpl, Router, routes} from 'chrome://settings/settings.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise, isChildVisible, isVisible, whenAttributeIs} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/settings/safety_hub_module_test.ts b/chrome/test/data/webui/settings/safety_hub_module_test.ts
index ee0a201..283d199 100644
--- a/chrome/test/data/webui/settings/safety_hub_module_test.ts
+++ b/chrome/test/data/webui/settings/safety_hub_module_test.ts
@@ -8,8 +8,8 @@
 import type {SettingsSafetyHubModuleElement} from 'chrome://settings/lazy_load.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {loadTimeData} from 'chrome://settings/settings.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/search_engines_page_test.ts b/chrome/test/data/webui/settings/search_engines_page_test.ts
index fcb50fb..11e92184 100644
--- a/chrome/test/data/webui/settings/search_engines_page_test.ts
+++ b/chrome/test/data/webui/settings/search_engines_page_test.ts
@@ -6,11 +6,10 @@
 import 'chrome://settings/lazy_load.js';
 
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {CrInputElement, SettingsSearchEngineEditDialogElement, SettingsSearchEnginesListElement, SettingsSearchEnginesPageElement} from 'chrome://settings/lazy_load.js';
 import type {SearchEngine, SearchEnginesInfo} from 'chrome://settings/settings.js';
-import {SearchEnginesBrowserProxyImpl, SearchEnginesInteractions} from 'chrome://settings/settings.js';
+import {loadTimeData, SearchEnginesBrowserProxyImpl, SearchEnginesInteractions} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/settings/secure_dns_interactive_test.ts b/chrome/test/data/webui/settings/secure_dns_interactive_test.ts
index 97d0a4f..a49e94b 100644
--- a/chrome/test/data/webui/settings/secure_dns_interactive_test.ts
+++ b/chrome/test/data/webui/settings/secure_dns_interactive_test.ts
@@ -11,12 +11,11 @@
 import 'chrome://settings/lazy_load.js';
 
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {SecureDnsInputElement, SettingsSecureDnsElement, SettingsToggleButtonElement} from 'chrome://settings/lazy_load.js';
 import {SecureDnsResolverType} from 'chrome://settings/lazy_load.js';
 import type {ResolverOption} from 'chrome://settings/settings.js';
-import {PrivacyPageBrowserProxyImpl, SecureDnsMode, SecureDnsUiManagementMode} from 'chrome://settings/settings.js';
+import {loadTimeData, PrivacyPageBrowserProxyImpl, SecureDnsMode, SecureDnsUiManagementMode} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
diff --git a/chrome/test/data/webui/settings/security_keys_set_pin_dialog_test.ts b/chrome/test/data/webui/settings/security_keys_set_pin_dialog_test.ts
index 2e5c9be..0dee0d9 100644
--- a/chrome/test/data/webui/settings/security_keys_set_pin_dialog_test.ts
+++ b/chrome/test/data/webui/settings/security_keys_set_pin_dialog_test.ts
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
+import {loadTimeData} from 'chrome://settings/settings.js';
 import type {CrInputElement, SecurityKeysPinBrowserProxy, SettingsSecurityKeysSetPinDialogElement} from 'chrome://settings/lazy_load.js';
 import {SecurityKeysPinBrowserProxyImpl, SetPinDialogPage} from 'chrome://settings/lazy_load.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/settings/security_page_test.ts b/chrome/test/data/webui/settings/security_page_test.ts
index e93a77d..d916c56 100644
--- a/chrome/test/data/webui/settings/security_page_test.ts
+++ b/chrome/test/data/webui/settings/security_page_test.ts
@@ -3,13 +3,12 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {SettingsSecurityPageElement} from 'chrome://settings/lazy_load.js';
 import {ContentSetting, ContentSettingsTypes, DefaultSettingSource, SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {HttpsFirstModeSetting, SafeBrowsingSetting} from 'chrome://settings/lazy_load.js';
 import type {CrLinkRowElement, SettingsPrefsElement, SettingsToggleButtonElement} from 'chrome://settings/settings.js';
-import {HatsBrowserProxyImpl, CrSettingsPrefs, MetricsBrowserProxyImpl, OpenWindowProxyImpl, PrivacyElementInteractions, PrivacyPageBrowserProxyImpl, resetRouterForTesting, Router, routes, SafeBrowsingInteractions, SecureDnsMode, SecurityPageInteraction} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, HatsBrowserProxyImpl, loadTimeData, MetricsBrowserProxyImpl, OpenWindowProxyImpl, PrivacyElementInteractions, PrivacyPageBrowserProxyImpl, resetRouterForTesting, Router, routes, SafeBrowsingInteractions, SecureDnsMode, SecurityPageInteraction} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue, assertNotEquals} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible, eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/settings/settings_main_test.ts b/chrome/test/data/webui/settings/settings_main_test.ts
index 70c4f8c..7274a9ff 100644
--- a/chrome/test/data/webui/settings/settings_main_test.ts
+++ b/chrome/test/data/webui/settings/settings_main_test.ts
@@ -3,10 +3,9 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {SettingsMainElement, SettingsPrefsElement} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs, Router, routes, setSearchManagerForTesting} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData, Router, routes, setSearchManagerForTesting} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {TestSearchManager} from './test_search_manager.js';
diff --git a/chrome/test/data/webui/settings/settings_toggle_button_test.ts b/chrome/test/data/webui/settings/settings_toggle_button_test.ts
index 141cb3a4..722ecad 100644
--- a/chrome/test/data/webui/settings/settings_toggle_button_test.ts
+++ b/chrome/test/data/webui/settings/settings_toggle_button_test.ts
@@ -5,10 +5,9 @@
 // clang-format off
 import 'chrome://settings/settings.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {SettingsToggleButtonElement} from 'chrome://settings/settings.js';
-import {DEFAULT_CHECKED_VALUE, DEFAULT_UNCHECKED_VALUE} from 'chrome://settings/settings.js';
+import {DEFAULT_CHECKED_VALUE, DEFAULT_UNCHECKED_VALUE, loadTimeData} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 // clang-format on
diff --git a/chrome/test/data/webui/settings/site_details_test.ts b/chrome/test/data/webui/settings/site_details_test.ts
index 4c544fb..b3bb3c77 100644
--- a/chrome/test/data/webui/settings/site_details_test.ts
+++ b/chrome/test/data/webui/settings/site_details_test.ts
@@ -8,11 +8,10 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {SiteDetailsElement, WebsiteUsageBrowserProxy} from 'chrome://settings/lazy_load.js';
 import {ChooserType, ContentSetting, ContentSettingsTypes, SiteSettingSource, SiteSettingsPrefsBrowserProxyImpl, WebsiteUsageBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
-import {MetricsBrowserProxyImpl, PrivacyElementInteractions, Router, routes} from 'chrome://settings/settings.js';
+import {loadTimeData, MetricsBrowserProxyImpl, PrivacyElementInteractions, Router, routes} from 'chrome://settings/settings.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 
 import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
 import {TestSiteSettingsPrefsBrowserProxy} from './test_site_settings_prefs_browser_proxy.js';
diff --git a/chrome/test/data/webui/settings/site_list_entry_test.ts b/chrome/test/data/webui/settings/site_list_entry_test.ts
index 0aca595..507e9c8 100644
--- a/chrome/test/data/webui/settings/site_list_entry_test.ts
+++ b/chrome/test/data/webui/settings/site_list_entry_test.ts
@@ -7,11 +7,10 @@
 // clang-format off
 import 'chrome://webui-test/cr_elements/cr_policy_strings.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {SiteListEntryElement} from 'chrome://settings/lazy_load.js';
 import {ContentSetting, ContentSettingsTypes, CookiesExceptionType, SITE_EXCEPTION_WILDCARD, SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
-import {Router, routes} from 'chrome://settings/settings.js';
+import {loadTimeData, Router, routes} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, isChildVisible} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/settings/site_settings_page_test.ts b/chrome/test/data/webui/settings/site_settings_page_test.ts
index c0f7a94..6c8aa44 100644
--- a/chrome/test/data/webui/settings/site_settings_page_test.ts
+++ b/chrome/test/data/webui/settings/site_settings_page_test.ts
@@ -4,12 +4,11 @@
 
 // clang-format off
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {CrExpandButtonElement, SettingsSiteSettingsPageElement} from 'chrome://settings/lazy_load.js';
 import {ContentSetting, CookieControlsMode, ContentSettingsTypes, defaultSettingLabel, SettingsState, SafetyHubBrowserProxyImpl, SafetyHubEvent} from 'chrome://settings/lazy_load.js';
 import type {CrLinkRowElement, SettingsToggleButtonElement} from 'chrome://settings/settings.js';
-import {Router, routes} from 'chrome://settings/settings.js';
+import {loadTimeData, Router, routes} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible} from 'chrome://webui-test/test_util.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/settings/sync_account_control_test.ts b/chrome/test/data/webui/settings/sync_account_control_test.ts
index fb45837..175e0c8 100644
--- a/chrome/test/data/webui/settings/sync_account_control_test.ts
+++ b/chrome/test/data/webui/settings/sync_account_control_test.ts
@@ -4,10 +4,9 @@
 
 import 'chrome://settings/settings.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {CrActionMenuElement, SettingsSyncAccountControlElement, StoredAccount} from 'chrome://settings/settings.js';
-import {resetRouterForTesting, Router, SignedInState, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
+import {loadTimeData, resetRouterForTesting, Router, SignedInState, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
 
diff --git a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.ts b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.ts
index a1e5134..b08a037 100644
--- a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.ts
+++ b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.ts
@@ -8,9 +8,9 @@
 import type {StorageAccessSiteException, AppProtocolEntry, ChooserType, HandlerEntry, OriginFileSystemGrants, ProtocolEntry, RawChooserException, RawSiteException, RecentSitePermissions, SiteGroup, SiteSettingsPrefsBrowserProxy, ZoomLevelEntry} from 'chrome://settings/lazy_load.js';
 import {ContentSetting, ContentSettingsTypes, SiteSettingSource} from 'chrome://settings/lazy_load.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 
 import type {SiteSettingsPref} from './test_util.js';
+import {loadTimeData} from 'chrome://settings/settings.js';
 import {createOriginInfo, createSiteGroup,createSiteSettingsPrefs, getContentSettingsTypeFromChooserType} from './test_util.js';
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/your_saved_info_page_test.ts b/chrome/test/data/webui/settings/your_saved_info_page_test.ts
index 6190159..5dbb9199 100644
--- a/chrome/test/data/webui/settings/your_saved_info_page_test.ts
+++ b/chrome/test/data/webui/settings/your_saved_info_page_test.ts
@@ -4,12 +4,11 @@
 
 import 'chrome://settings/settings.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import type {SettingsYourSavedInfoPageElement} from 'chrome://settings/settings.js';
 import {AutofillManagerImpl, PaymentsManagerImpl} from 'chrome://settings/lazy_load.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {PasswordManagerImpl, PasswordManagerPage, Router, routes} from 'chrome://settings/settings.js';
+import {loadTimeData, PasswordManagerImpl, PasswordManagerPage, Router, routes} from 'chrome://settings/settings.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {createAddressEntry, createCreditCardEntry, createIbanEntry, createPayOverTimeIssuerEntry, TestAutofillManager, TestPaymentsManager} from './autofill_fake_data.js';
diff --git a/chrome/test/data/webui/side_panel/read_anything/dom_read_aloud_node_test.ts b/chrome/test/data/webui/side_panel/read_anything/dom_read_aloud_node_test.ts
index d89fc50..7b18000 100644
--- a/chrome/test/data/webui/side_panel/read_anything/dom_read_aloud_node_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/dom_read_aloud_node_test.ts
@@ -4,7 +4,7 @@
 import 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
 
 import {DomReadAloudNode, ReadAloudNode} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
-import {assertEquals, assertNotEquals, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
+import {assertEquals, assertNotEquals, assertTrue, assertFalse} from 'chrome-untrusted://webui-test/chai_assert.js';
 
 suite('DomReadAloudNode', () => {
   setup(() => {
@@ -65,4 +65,38 @@
     // block ancestor.
     ancestorSame([textBeforeNode, boldNode, textAfterNode]);
   });
+
+  test('equals', () => {
+    // Create the first DOM node
+    const div1 = document.createElement('div');
+    const paragraph1 = document.createElement('p');
+    paragraph1.textContent = 'This is it, hit the break!';
+    div1.appendChild(paragraph1);
+
+    // Create a second, structurally identical DOM node
+    const div2 = document.createElement('div');
+    const paragraph2 = document.createElement('p');
+    paragraph2.textContent = 'This is it, hit the break!';
+    div2.appendChild(paragraph2);
+
+    document.body.appendChild(div1);
+    document.body.appendChild(div2);
+
+    // Check if they are structurally equal.
+    assertTrue(div1.isEqualNode(div2));
+
+    const node1 = ReadAloudNode.create(div1)!;
+    const node2 = ReadAloudNode.create(div2)!;
+    const node3 = ReadAloudNode.create(div1)!;
+
+    // node1 === node3, since they both were created with div1.
+    assertTrue(node1.equals(node3));
+    assertTrue(node3.equals(node1));
+
+    // node1 !== node2, even though the nodes are structurally equal, because
+    // div1 is not the same in memory as div2.
+    assertFalse(node1.equals(node2));
+    assertFalse(node2.equals(node1));
+  });
+
 });
diff --git a/chrome/updater/fix_licenses.py b/chrome/updater/fix_licenses.py
index 2098a66..6a555f5 100755
--- a/chrome/updater/fix_licenses.py
+++ b/chrome/updater/fix_licenses.py
@@ -29,7 +29,7 @@
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
+the Free Software Foundation, either version 2 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful, but
diff --git a/chrome/updater/win/task_scheduler.cc b/chrome/updater/win/task_scheduler.cc
index 8b3477e..a85b1c7 100644
--- a/chrome/updater/win/task_scheduler.cc
+++ b/chrome/updater/win/task_scheduler.cc
@@ -471,7 +471,7 @@
               }
 
               std::optional<std::wstring> new_sddl =
-                  AddCurrentUserAllowedAce(sddl.Get(), GENERIC_ALL, 0);
+                  AddCurrentUserAllowedAce(sddl.Get(), FILE_ALL_ACCESS, 0);
               if (!new_sddl) {
                 return;
               }
diff --git a/chromecast/media/service/BUILD.gn b/chromecast/media/service/BUILD.gn
index 0f90a349..4e4e9d1c 100644
--- a/chromecast/media/service/BUILD.gn
+++ b/chromecast/media/service/BUILD.gn
@@ -35,6 +35,7 @@
       "create_mojo_media_client_starboard.cc",
     ]
     deps += [
+      "//chromecast/base/metrics",
       "//chromecast/starboard/media/media:starboard_api_wrapper",
       "//chromecast/starboard/media/renderer:starboard_renderer",
     ]
diff --git a/chromecast/media/service/cast_starboard_mojo_media_client.cc b/chromecast/media/service/cast_starboard_mojo_media_client.cc
index 81049ebff..73cf6b4 100644
--- a/chromecast/media/service/cast_starboard_mojo_media_client.cc
+++ b/chromecast/media/service/cast_starboard_mojo_media_client.cc
@@ -9,6 +9,7 @@
 
 #include "base/check.h"
 #include "base/logging.h"
+#include "chromecast/base/metrics/cast_metrics_helper.h"
 #include "chromecast/starboard/media/media/starboard_api_wrapper.h"
 #include "chromecast/starboard/media/renderer/starboard_renderer.h"
 
@@ -43,7 +44,8 @@
   LOG(INFO) << "Using StarboardRenderer";
   return std::make_unique<StarboardRenderer>(
       chromecast::media::GetStarboardApiWrapper(), std::move(task_runner),
-      overlay_plane_id, enable_buffering_cb_.Run(), video_geometry_setter_);
+      overlay_plane_id, enable_buffering_cb_.Run(), video_geometry_setter_,
+      chromecast::metrics::CastMetricsHelper::GetInstance());
 }
 
 std::unique_ptr<::media::Renderer> CastStarboardMojoMediaClient::CreateRenderer(
diff --git a/chromecast/starboard/chromecast/events/starboard_event_source.cc b/chromecast/starboard/chromecast/events/starboard_event_source.cc
index ee06a85..dc827637 100644
--- a/chromecast/starboard/chromecast/events/starboard_event_source.cc
+++ b/chromecast/starboard/chromecast/events/starboard_event_source.cc
@@ -4,11 +4,25 @@
 
 #include "chromecast/starboard/chromecast/events/starboard_event_source.h"
 
-#include "base/containers/fixed_flat_map.h"
+#include <cmath>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include "base/containers/flat_map.h"
 #include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/time/time.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/dom/dom_key.h"
 #include "ui/events/keycodes/keyboard_code_conversion.h"
+#include "ui/events/pointer_details.h"
+#include "ui/events/types/event_type.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/gfx/geometry/vector2d_f.h"
 
 namespace chromecast {
 
@@ -17,50 +31,276 @@
 constexpr char kPropertyFromStarboard[] = "from_sb";
 constexpr size_t kPropertyFromStarboardSize = 1;
 
-// This map represents the key codes that can be recognized by Cast. The
-// resulting DomCodes are the union of:
-// - Keys which are used by the Cast SDK.
-// - Keys which are used by the Cast SDK when the DPAD UI is enabled.
-// - Keys which are not used by the Cast SDK, but are defined in the HDMI CEC
-//   specification and may be useful to apps.
-constexpr auto kSbKeyToDomCodeMap = base::MakeFixedFlatMap<SbKey, ui::DomCode>({
-    // Convenience keys for keyboard support.
-    {kSbKeySpace, ui::DomCode::MEDIA_PLAY_PAUSE},
+const base::flat_map<SbKey, ui::DomCode>& GetSbKeyToDomCodeMap() {
+  static const base::NoDestructor<base::flat_map<SbKey, ui::DomCode>> kMap({
+      // Common Keys
+      {kSbKeySpace, ui::DomCode::SPACE},
+      {kSbKeyReturn, ui::DomCode::ENTER},
+      {kSbKeySelect, ui::DomCode::SELECT},
+      {kSbKeyUp, ui::DomCode::ARROW_UP},
+      {kSbKeyDown, ui::DomCode::ARROW_DOWN},
+      {kSbKeyLeft, ui::DomCode::ARROW_LEFT},
+      {kSbKeyRight, ui::DomCode::ARROW_RIGHT},
+      {kSbKeyBack, ui::DomCode::BROWSER_BACK},
+      {kSbKeyEscape, ui::DomCode::ESCAPE},
+      {kSbKeyTab, ui::DomCode::TAB},
+      {kSbKeyBacktab,
+       ui::DomCode::TAB},  // No distinct backtab in DomCode, handle with Shift
+      {kSbKeyOem1, ui::DomCode::SEMICOLON},
+      {kSbKeyOemPlus, ui::DomCode::EQUAL},
+      {kSbKeyOemComma, ui::DomCode::COMMA},
+      {kSbKeyOemMinus, ui::DomCode::MINUS},
+      {kSbKeyOemPeriod, ui::DomCode::PERIOD},
+      {kSbKeyOem2, ui::DomCode::SLASH},
+      {kSbKeyOem3, ui::DomCode::BACKQUOTE},
+      {kSbKeyOem4, ui::DomCode::BRACKET_LEFT},
+      {kSbKeyOem5, ui::DomCode::BACKSLASH},
+      {kSbKeyOem6, ui::DomCode::BRACKET_RIGHT},
+      {kSbKeyOem7, ui::DomCode::QUOTE},
 
-    // Keys which are used by the Cast SDK.
-    {kSbKeyReturn, ui::DomCode::ENTER},
-    {kSbKeySelect, ui::DomCode::SELECT},
-    {kSbKeyUp, ui::DomCode::ARROW_UP},
-    {kSbKeyDown, ui::DomCode::ARROW_DOWN},
-    {kSbKeyLeft, ui::DomCode::ARROW_LEFT},
-    {kSbKeyRight, ui::DomCode::ARROW_RIGHT},
-    {kSbKeyBack, ui::DomCode::BROWSER_BACK},
-    {kSbKeyEscape, ui::DomCode::BROWSER_BACK},
-
-
-    // Keys which are used by the Cast SDK when the DPAD UI is enabled.
-    {kSbKeyMediaPlayPause, ui::DomCode::MEDIA_PLAY_PAUSE},
-    {kSbKeyMediaRewind, ui::DomCode::MEDIA_REWIND},
-    {kSbKeyMediaFastForward, ui::DomCode::MEDIA_FAST_FORWARD},
-    {kSbKeyMediaNextTrack, ui::DomCode::MEDIA_TRACK_NEXT},
-    {kSbKeyMediaPrevTrack, ui::DomCode::MEDIA_TRACK_PREVIOUS},
-    {kSbKeyPause, ui::DomCode::MEDIA_PAUSE},
-    {kSbKeyPlay, ui::DomCode::MEDIA_PLAY},
-    {kSbKeyMediaStop, ui::DomCode::MEDIA_STOP},
-
-    // Keys which are not used by the Cast SDK, but are defined in the HDMI CEC
-    // specification.
-    {kSbKeyMenu, ui::DomCode::HOME},
-    {kSbKeyChannelUp, ui::DomCode::CHANNEL_UP},
-    {kSbKeyChannelDown, ui::DomCode::CHANNEL_DOWN},
-    {kSbKeyClosedCaption, ui::DomCode::CLOSED_CAPTION_TOGGLE},
+      // Media Keys
+      {kSbKeyMediaPlayPause, ui::DomCode::MEDIA_PLAY_PAUSE},
+      {kSbKeyMediaRewind, ui::DomCode::MEDIA_REWIND},
+      {kSbKeyMediaFastForward, ui::DomCode::MEDIA_FAST_FORWARD},
+      {kSbKeyMediaNextTrack, ui::DomCode::MEDIA_TRACK_NEXT},
+      {kSbKeyMediaPrevTrack, ui::DomCode::MEDIA_TRACK_PREVIOUS},
+      {kSbKeyPause, ui::DomCode::PAUSE},
+      {kSbKeyPlay, ui::DomCode::MEDIA_PLAY},
+      {kSbKeyMediaStop, ui::DomCode::MEDIA_STOP},
+      {kSbKeyChannelUp, ui::DomCode::CHANNEL_UP},
+      {kSbKeyChannelDown, ui::DomCode::CHANNEL_DOWN},
+      {kSbKeyClosedCaption, ui::DomCode::CLOSED_CAPTION_TOGGLE},
 #if SB_API_VERSION >= 15
-    {kSbKeyRecord, ui::DomCode::MEDIA_RECORD},
+      {kSbKeyRecord, ui::DomCode::MEDIA_RECORD},
 #endif  // SB_API_VERSION >=15
-});
+      {kSbKeyVolumeUp, ui::DomCode::VOLUME_UP},
+      {kSbKeyVolumeDown, ui::DomCode::VOLUME_DOWN},
+      {kSbKeyVolumeMute, ui::DomCode::VOLUME_MUTE},
 
-// Returns the current SequencedTaskRunner. Crashes if not called from a
-// sequenced task runner.
+      // Alphabet
+      {kSbKeyA, ui::DomCode::US_A},
+      {kSbKeyB, ui::DomCode::US_B},
+      {kSbKeyC, ui::DomCode::US_C},
+      {kSbKeyD, ui::DomCode::US_D},
+      {kSbKeyE, ui::DomCode::US_E},
+      {kSbKeyF, ui::DomCode::US_F},
+      {kSbKeyG, ui::DomCode::US_G},
+      {kSbKeyH, ui::DomCode::US_H},
+      {kSbKeyI, ui::DomCode::US_I},
+      {kSbKeyJ, ui::DomCode::US_J},
+      {kSbKeyK, ui::DomCode::US_K},
+      {kSbKeyL, ui::DomCode::US_L},
+      {kSbKeyM, ui::DomCode::US_M},
+      {kSbKeyN, ui::DomCode::US_N},
+      {kSbKeyO, ui::DomCode::US_O},
+      {kSbKeyP, ui::DomCode::US_P},
+      {kSbKeyQ, ui::DomCode::US_Q},
+      {kSbKeyR, ui::DomCode::US_R},
+      {kSbKeyS, ui::DomCode::US_S},
+      {kSbKeyT, ui::DomCode::US_T},
+      {kSbKeyU, ui::DomCode::US_U},
+      {kSbKeyV, ui::DomCode::US_V},
+      {kSbKeyW, ui::DomCode::US_W},
+      {kSbKeyX, ui::DomCode::US_X},
+      {kSbKeyY, ui::DomCode::US_Y},
+      {kSbKeyZ, ui::DomCode::US_Z},
+
+      // Digits
+      {kSbKey0, ui::DomCode::DIGIT0},
+      {kSbKey1, ui::DomCode::DIGIT1},
+      {kSbKey2, ui::DomCode::DIGIT2},
+      {kSbKey3, ui::DomCode::DIGIT3},
+      {kSbKey4, ui::DomCode::DIGIT4},
+      {kSbKey5, ui::DomCode::DIGIT5},
+      {kSbKey6, ui::DomCode::DIGIT6},
+      {kSbKey7, ui::DomCode::DIGIT7},
+      {kSbKey8, ui::DomCode::DIGIT8},
+      {kSbKey9, ui::DomCode::DIGIT9},
+
+      // Numpad Digits
+      {kSbKeyNumpad0, ui::DomCode::NUMPAD0},
+      {kSbKeyNumpad1, ui::DomCode::NUMPAD1},
+      {kSbKeyNumpad2, ui::DomCode::NUMPAD2},
+      {kSbKeyNumpad3, ui::DomCode::NUMPAD3},
+      {kSbKeyNumpad4, ui::DomCode::NUMPAD4},
+      {kSbKeyNumpad5, ui::DomCode::NUMPAD5},
+      {kSbKeyNumpad6, ui::DomCode::NUMPAD6},
+      {kSbKeyNumpad7, ui::DomCode::NUMPAD7},
+      {kSbKeyNumpad8, ui::DomCode::NUMPAD8},
+      {kSbKeyNumpad9, ui::DomCode::NUMPAD9},
+
+      // Numpad Others
+      {kSbKeyMultiply, ui::DomCode::NUMPAD_MULTIPLY},
+      {kSbKeyAdd, ui::DomCode::NUMPAD_ADD},
+      {kSbKeySubtract, ui::DomCode::NUMPAD_SUBTRACT},
+      {kSbKeyDecimal, ui::DomCode::NUMPAD_DECIMAL},
+      {kSbKeyDivide, ui::DomCode::NUMPAD_DIVIDE},
+      {kSbKeyNumlock, ui::DomCode::NUM_LOCK},
+
+      // Function Keys
+      {kSbKeyF1, ui::DomCode::F1},
+      {kSbKeyF2, ui::DomCode::F2},
+      {kSbKeyF3, ui::DomCode::F3},
+      {kSbKeyF4, ui::DomCode::F4},
+      {kSbKeyF5, ui::DomCode::F5},
+      {kSbKeyF6, ui::DomCode::F6},
+      {kSbKeyF7, ui::DomCode::F7},
+      {kSbKeyF8, ui::DomCode::F8},
+      {kSbKeyF9, ui::DomCode::F9},
+      {kSbKeyF10, ui::DomCode::F10},
+      {kSbKeyF11, ui::DomCode::F11},
+      {kSbKeyF12, ui::DomCode::F12},
+
+      // Modifiers
+      {kSbKeyShift, ui::DomCode::SHIFT_LEFT},
+      {kSbKeyLshift, ui::DomCode::SHIFT_LEFT},
+      {kSbKeyRshift, ui::DomCode::SHIFT_RIGHT},
+      {kSbKeyControl, ui::DomCode::CONTROL_LEFT},
+      {kSbKeyLcontrol, ui::DomCode::CONTROL_LEFT},
+      {kSbKeyRcontrol, ui::DomCode::CONTROL_RIGHT},
+      {kSbKeyMenu, ui::DomCode::ALT_LEFT},
+      {kSbKeyLmenu, ui::DomCode::ALT_LEFT},
+      {kSbKeyRmenu, ui::DomCode::ALT_RIGHT},
+      {kSbKeyLwin, ui::DomCode::META_LEFT},
+      {kSbKeyRwin, ui::DomCode::META_RIGHT},
+      {kSbKeyApps, ui::DomCode::CONTEXT_MENU},
+
+      // Other common keys
+      {kSbKeyCapital, ui::DomCode::CAPS_LOCK},
+      {kSbKeyBackspace, ui::DomCode::BACKSPACE},
+      {kSbKeyDelete, ui::DomCode::DEL},
+      {kSbKeyInsert, ui::DomCode::INSERT},
+      {kSbKeyHome, ui::DomCode::HOME},
+      {kSbKeyEnd, ui::DomCode::END},
+      {kSbKeyPrior, ui::DomCode::PAGE_UP},
+      {kSbKeyNext, ui::DomCode::PAGE_DOWN},
+      {kSbKeyPrint, ui::DomCode::PRINT_SCREEN},
+      {kSbKeySnapshot, ui::DomCode::PRINT_SCREEN},
+      {kSbKeyScroll, ui::DomCode::SCROLL_LOCK},
+
+      // Browser Keys
+      {kSbKeyBrowserBack, ui::DomCode::BROWSER_BACK},
+      {kSbKeyBrowserForward, ui::DomCode::BROWSER_FORWARD},
+      {kSbKeyBrowserRefresh, ui::DomCode::BROWSER_REFRESH},
+      {kSbKeyBrowserStop, ui::DomCode::BROWSER_STOP},
+      {kSbKeyBrowserSearch, ui::DomCode::BROWSER_SEARCH},
+      {kSbKeyBrowserFavorites, ui::DomCode::BROWSER_FAVORITES},
+      {kSbKeyBrowserHome, ui::DomCode::BROWSER_HOME},
+  });
+  return *kMap;
+}
+
+// Helper function to map Starboard modifiers to ui::EventFlags
+int MapStarboardModifiersToUiFlags(unsigned int sb_modifiers) {
+  int ui_flags = 0;
+  if (sb_modifiers & kSbKeyModifiersAlt) {
+    ui_flags |= ui::EF_ALT_DOWN;
+  }
+  if (sb_modifiers & kSbKeyModifiersCtrl) {
+    ui_flags |= ui::EF_CONTROL_DOWN;
+  }
+  if (sb_modifiers & kSbKeyModifiersMeta) {
+    ui_flags |= ui::EF_COMMAND_DOWN;
+  }
+  if (sb_modifiers & kSbKeyModifiersShift) {
+    ui_flags |= ui::EF_SHIFT_DOWN;
+  }
+
+  if (sb_modifiers & kSbKeyModifiersPointerButtonLeft) {
+    ui_flags |= ui::EF_LEFT_MOUSE_BUTTON;
+  }
+  if (sb_modifiers & kSbKeyModifiersPointerButtonRight) {
+    ui_flags |= ui::EF_RIGHT_MOUSE_BUTTON;
+  }
+  if (sb_modifiers & kSbKeyModifiersPointerButtonMiddle) {
+    ui_flags |= ui::EF_MIDDLE_MOUSE_BUTTON;
+  }
+  if (sb_modifiers & kSbKeyModifiersPointerButtonBack) {
+    ui_flags |= ui::EF_BACK_MOUSE_BUTTON;
+  }
+  if (sb_modifiers & kSbKeyModifiersPointerButtonForward) {
+    ui_flags |= ui::EF_FORWARD_MOUSE_BUTTON;
+  }
+  return ui_flags;
+}
+
+// Helper to create Touch events
+std::unique_ptr<ui::Event> CreateTouchEvent(const SbInputData* input_data,
+                                            base::TimeTicks timestamp,
+                                            int ui_flags) {
+  const SbInputEventType raw_type = input_data->type;
+  const gfx::PointF position(input_data->position.x, input_data->position.y);
+
+  ui::EventType event_type;
+  switch (raw_type) {
+    case kSbInputEventTypePress:
+      event_type = ui::EventType::kTouchPressed;
+      break;
+    case kSbInputEventTypeUnpress:
+      event_type = ui::EventType::kTouchReleased;
+      break;
+    case kSbInputEventTypeMove:
+      event_type = ui::EventType::kTouchMoved;
+      break;
+    default:
+      VLOG(1) << "Unhandled Touch EventType: " << raw_type;
+      return nullptr;
+  }
+
+  // SbInputData currently only supports single touch, so pointer_id is
+  // hardcoded to 0.
+  ui::PointerDetails pointer_details(ui::EventPointerType::kTouch,
+                                     /*pointer_id=*/0,
+                                     /*radius_x=*/input_data->size.x / 2.0f,
+                                     /*radius_y=*/input_data->size.y / 2.0f,
+                                     /*force=*/input_data->pressure);
+
+  if (!std::isnan(input_data->tilt.x) && !std::isnan(input_data->tilt.y)) {
+    pointer_details.tilt_x = input_data->tilt.x;
+    pointer_details.tilt_y = input_data->tilt.y;
+  }
+
+  return std::make_unique<ui::TouchEvent>(event_type, position, position,
+                                          timestamp, pointer_details, ui_flags);
+}
+
+// Helper to create Key events
+std::unique_ptr<ui::Event> CreateKeyEvent(const SbInputData* input_data,
+                                          base::TimeTicks timestamp,
+                                          int ui_flags) {
+  const SbInputEventType raw_type = input_data->type;
+  const SbKey raw_key = input_data->key;
+
+  if (raw_type != kSbInputEventTypePress &&
+      raw_type != kSbInputEventTypeUnpress) {
+    return nullptr;
+  }
+
+  const auto& key_map = GetSbKeyToDomCodeMap();
+  auto it = key_map.find(raw_key);
+  if (it != key_map.end()) {
+    ui::DomCode dom_code = it->second;
+    ui::DomKey dom_key;
+    ui::KeyboardCode key_code;
+    int current_flags = ui_flags;
+    if (raw_key == kSbKeyBacktab) {
+      current_flags |= ui::EF_SHIFT_DOWN;
+    }
+
+    if (ui::DomCodeToUsLayoutDomKey(dom_code, current_flags, &dom_key,
+                                    &key_code)) {
+      ui::EventType event_type = (raw_type == kSbInputEventTypePress)
+                                     ? ui::EventType::kKeyPressed
+                                     : ui::EventType::kKeyReleased;
+      return std::make_unique<ui::KeyEvent>(event_type, key_code, dom_code,
+                                            current_flags, dom_key, timestamp);
+    }
+  } else {
+    VLOG(1) << "Unhandled SbKey: " << raw_key;
+  }
+  return nullptr;
+}
+
 scoped_refptr<base::SequencedTaskRunner> GetCurrentSequencedTaskRunner() {
   CHECK(base::SequencedTaskRunner::HasCurrentDefault());
   return base::SequencedTaskRunner::GetCurrentDefault();
@@ -68,6 +308,73 @@
 
 }  // namespace
 
+// Helper to create Mouse events
+std::unique_ptr<ui::Event> StarboardEventSource::CreateMouseEvent(
+    const SbInputData* input_data,
+    base::TimeTicks timestamp,
+    int ui_flags) {
+  const SbInputEventType raw_type = input_data->type;
+  const SbKey raw_key = input_data->key;
+  const gfx::PointF position(input_data->position.x, input_data->position.y);
+  switch (raw_type) {
+    case kSbInputEventTypePress:
+    case kSbInputEventTypeUnpress: {
+      int changed_button_flag = 0;
+      switch (raw_key) {
+        case kSbKeyMouse1:
+          changed_button_flag = ui::EF_LEFT_MOUSE_BUTTON;
+          break;
+        case kSbKeyMouse2:
+          changed_button_flag = ui::EF_RIGHT_MOUSE_BUTTON;
+          break;
+        case kSbKeyMouse3:
+          changed_button_flag = ui::EF_MIDDLE_MOUSE_BUTTON;
+          break;
+        case kSbKeyMouse4:
+          changed_button_flag = ui::EF_BACK_MOUSE_BUTTON;
+          break;
+        case kSbKeyMouse5:
+          changed_button_flag = ui::EF_FORWARD_MOUSE_BUTTON;
+          break;
+        default:
+          return nullptr;  // Not a mouse button key
+      }
+
+      // Starboard doesn't have a concept of dragging, which is conventionally
+      // required by the platform. Simulate it by tracking mouse presses.
+      // ui::EventFlags are used because they are bit flags.
+      key_flags_ ^= changed_button_flag;
+      ui::EventType event_type = (raw_type == kSbInputEventTypePress)
+                                     ? ui::EventType::kMousePressed
+                                     : ui::EventType::kMouseReleased;
+      return std::make_unique<ui::MouseEvent>(
+          event_type, position, position, timestamp,
+          ui_flags | key_flags_ | changed_button_flag, changed_button_flag);
+    }
+    case kSbInputEventTypeMove: {
+      ui::EventType event_type = key_flags_ ? ui::EventType::kMouseDragged
+                                            : ui::EventType::kMouseMoved;
+      return std::make_unique<ui::MouseEvent>(
+          event_type, position, position, timestamp, ui_flags | key_flags_, 0);
+    }
+    case kSbInputEventTypeWheel: {
+      // Standard multiplier for converting line deltas to scroll offsets.
+      const int kScrollOffsetMultiplier = ui::MouseWheelEvent::kWheelDelta;
+      int offset_x =
+          static_cast<int>(-input_data->delta.x * kScrollOffsetMultiplier);
+      int offset_y =
+          static_cast<int>(-input_data->delta.y * kScrollOffsetMultiplier);
+
+      return std::make_unique<ui::MouseWheelEvent>(
+          gfx::Vector2d(offset_x, offset_y), position, position, timestamp,
+          ui_flags | key_flags_, ui::EF_NONE);
+    }
+    default:
+      VLOG(1) << "Unhandled Mouse EventType: " << raw_type;
+      return nullptr;
+  }
+}
+
 // static
 void StarboardEventSource::SbEventHandle(void* context, const SbEvent* event) {
   reinterpret_cast<StarboardEventSource*>(context)->SbEventHandleInternal(
@@ -75,72 +382,40 @@
 }
 
 void StarboardEventSource::SbEventHandleInternal(const SbEvent* event) {
-  if (event->type != kSbEventTypeInput) {
+  if (event->type != kSbEventTypeInput || event->data == nullptr) {
     return;
   }
+  const auto* input_data = static_cast<const SbInputData*>(event->data);
 
-  if (event->data == nullptr) {
-    return;
-  }
-  auto* input_data = static_cast<SbInputData*>(event->data);
-
-  SbTimeMonotonic raw_timestamp = event->timestamp;
-  SbInputEventType raw_type = input_data->type;
-  SbKey raw_key = input_data->key;
-  SbInputVector raw_position = input_data->position;
-  if (raw_type != kSbInputEventTypePress &&
-      raw_type != kSbInputEventTypeUnpress &&
-      raw_type != kSbInputEventTypeMove) {
-    return;
-  }
-
-  // Find out if the press is supported by Cast.
-  auto it = kSbKeyToDomCodeMap.find(raw_key);
-  if (it == kSbKeyToDomCodeMap.end() && raw_key != kSbKeyMouse1 &&
-      raw_type != kSbInputEventTypeMove) {
-    return;
-  }
+  base::TimeTicks timestamp =
+      base::TimeTicks() + base::Microseconds(event->timestamp);
+  int ui_flags = MapStarboardModifiersToUiFlags(input_data->key_modifiers);
 
   std::unique_ptr<ui::Event> ui_event;
-  ui::DomKey dom_key;
-  ui::KeyboardCode key_code;
-  ui::DomCode dom_code = it->second;
-  int flags = 0;
-  if (DomCodeToUsLayoutDomKey(dom_code, flags, &dom_key, &key_code)) {
-    // Key press.
-    ui::EventType event_type = raw_type == kSbInputEventTypePress
-                                   ? ui::EventType::kKeyPressed
-                                   : ui::EventType::kKeyReleased;
-    ui_event = std::make_unique<ui::KeyEvent>(
-        event_type, key_code, dom_code, flags, dom_key,
-        /*time_stamp=*/base::TimeTicks() + base::Microseconds(raw_timestamp));
-  } else if (raw_key == kSbKeyMouse1) {
-    // Mouse left click.
-    ui::EventType event_type = raw_type == kSbInputEventTypePress
-                                   ? ui::EventType::kMousePressed
-                                   : ui::EventType::kMouseReleased;
-    ui_event = std::make_unique<ui::MouseEvent>(
-        event_type, gfx::PointF(raw_position.x, raw_position.y),
-        gfx::PointF(raw_position.x, raw_position.y),
-        base::TimeTicks() + base::Microseconds(raw_timestamp),
-        ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
-  } else if (raw_type == kSbInputEventTypeMove) {
-    // Mouse move.
-    ui::EventType event_type = ui::EventType::kMouseMoved;
-    ui_event = std::make_unique<ui::MouseEvent>(
-        event_type, gfx::PointF(raw_position.x, raw_position.y),
-        gfx::PointF(raw_position.x, raw_position.y),
-        base::TimeTicks() + base::Microseconds(raw_timestamp), flags, flags);
-  } else {
-    // Unsupported by Cast.
-    return;
+  switch (input_data->device_type) {
+    case kSbInputDeviceTypeMouse:
+      ui_event = CreateMouseEvent(input_data, timestamp, ui_flags);
+      break;
+    case kSbInputDeviceTypeTouchScreen:
+      ui_event = CreateTouchEvent(input_data, timestamp, ui_flags);
+      break;
+    case kSbInputDeviceTypeKeyboard:
+    case kSbInputDeviceTypeRemote:
+      ui_event = CreateKeyEvent(input_data, timestamp, ui_flags);
+      break;
+    default:
+      VLOG(1) << "Ignoring event from device type: " << input_data->device_type;
+      break;
   }
 
-  ui::Event::Properties properties;
-  properties[chromecast::kPropertyFromStarboard] =
-      std::vector<uint8_t>(chromecast::kPropertyFromStarboardSize);
-  ui_event->SetProperties(properties);
-  DispatchUiEvent(std::move(ui_event));
+  if (ui_event) {
+    ui::Event::Properties properties;
+    properties[kPropertyFromStarboard] =
+        std::vector<uint8_t>(kPropertyFromStarboardSize);
+    ui_event->SetProperties(properties);
+
+    DispatchUiEvent(std::move(ui_event));
+  }
 }
 
 void StarboardEventSource::DispatchUiEvent(std::unique_ptr<ui::Event> event) {
diff --git a/chromecast/starboard/chromecast/events/starboard_event_source.h b/chromecast/starboard/chromecast/events/starboard_event_source.h
index cd2d977..88999d1 100644
--- a/chromecast/starboard/chromecast/events/starboard_event_source.h
+++ b/chromecast/starboard/chromecast/events/starboard_event_source.h
@@ -43,8 +43,15 @@
   // which |this| was created.
   void DispatchUiEvent(std::unique_ptr<ui::Event> event);
 
+  // Creates a MouseEvent for the |input_data| at the |timestamp| with the
+  // specified |ui_flags|. Stores or appends |key_flags_| based on press state.
+  std::unique_ptr<ui::Event> CreateMouseEvent(const SbInputData* input_data,
+                                              base::TimeTicks timestamp,
+                                              int ui_flags);
+
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   ui::PlatformWindowDelegate* delegate_;
+  int key_flags_ = 0;
 
   base::WeakPtrFactory<StarboardEventSource> weak_factory_{this};
 };
diff --git a/chromecast/starboard/chromecast/events/starboard_event_source_test.cc b/chromecast/starboard/chromecast/events/starboard_event_source_test.cc
index 7c1b588..7643105 100644
--- a/chromecast/starboard/chromecast/events/starboard_event_source_test.cc
+++ b/chromecast/starboard/chromecast/events/starboard_event_source_test.cc
@@ -2,7 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "starboard_event_source.h"
+#include "chromecast/starboard/chromecast/events/starboard_event_source.h"
+
+#include <cmath>
+#include <memory>
+#include <optional>
 
 #include "base/test/task_environment.h"
 #include "chromecast/starboard/chromecast/starboard_adapter/src/cast_starboard_api_adapter_impl.h"
@@ -10,7 +14,13 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/owned_window_anchor.h"
 #include "ui/events/event.h"
+#include "ui/events/event_constants.h"
 #include "ui/events/keycodes/dom/dom_code.h"
+#include "ui/events/pointer_details.h"
+#include "ui/events/types/event_type.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/vector2d.h"
 #include "ui/platform_window/platform_window_delegate.h"
 
 namespace chromecast {
@@ -71,7 +81,7 @@
               GetOwnedWindowAnchorAndRectInDIP,
               (),
               (override));
-  MOCK_METHOD(void, OnMouseEnter, (), (override));
+  MOCK_METHOD(void, OnCursorUpdate, (), (override));
   MOCK_METHOD(void,
               OnOcclusionStateChanged,
               (ui::PlatformWindowOcclusionState occlusion_state),
@@ -89,25 +99,36 @@
 };
 
 using ::testing::_;
+using ::testing::SaveArg;
 
-constexpr float kXPosClick = 1;
-constexpr float kYPosClick = 2;
-constexpr float kXPosMove = 3;
-constexpr float kYPosMove = 4;
+constexpr float kXPos = 1.0f;
+constexpr float kYPos = 2.0f;
+constexpr float kDeltaX = 3.0f;
+constexpr float kDeltaY = 4.0f;
+constexpr float kPressure = 0.5f;
+constexpr float kSizeX = 10.0f;
+constexpr float kSizeY = 12.0f;
+constexpr float kTiltX = 30.0f;
+constexpr float kTiltY = 40.0f;
 
 void DispatchSbEvent(const SbEvent* event) {
   // The |source_| should be subscribed to this and receive the event.
   CastStarboardApiAdapterImpl::SbEventHandle(event);
 }
 
-void EmulateKey(SbKey key, bool press) {
-  SbInputData input_data;
+void EmulateKey(SbKey key,
+                bool press,
+                SbInputDeviceType device_type = kSbInputDeviceTypeKeyboard,
+                unsigned int key_modifiers = kSbKeyModifiersNone) {
+  SbInputData input_data = {};
   input_data.type = press ? kSbInputEventTypePress : kSbInputEventTypeUnpress;
+  input_data.device_type = device_type;
   input_data.key = key;
+  input_data.key_modifiers = key_modifiers;
 
-  // Normally invalid for keys other than mouse presses, but okay for test.
-  input_data.position.x = kXPosClick;
-  input_data.position.y = kYPosClick;
+  // Position is mostly for mouse/touch, but harmless for keys.
+  input_data.position.x = kXPos;
+  input_data.position.y = kYPos;
 
   SbEvent event;
   event.type = kSbEventTypeInput;
@@ -116,11 +137,57 @@
   DispatchSbEvent(&event);
 }
 
-void EmulateMove() {
-  SbInputData input_data;
+void EmulateMouseMove(float x,
+                      float y,
+                      unsigned int key_modifiers = kSbKeyModifiersNone) {
+  SbInputData input_data = {};
   input_data.type = kSbInputEventTypeMove;
-  input_data.position.x = kXPosMove;
-  input_data.position.y = kYPosMove;
+  input_data.device_type = kSbInputDeviceTypeMouse;
+  input_data.position.x = x;
+  input_data.position.y = y;
+  input_data.key_modifiers = key_modifiers;
+
+  SbEvent event;
+  event.type = kSbEventTypeInput;
+  event.data = &input_data;
+
+  DispatchSbEvent(&event);
+}
+
+void EmulateMouseWheel(float delta_x,
+                       float delta_y,
+                       unsigned int key_modifiers = kSbKeyModifiersNone) {
+  SbInputData input_data = {};
+  input_data.type = kSbInputEventTypeWheel;
+  input_data.device_type = kSbInputDeviceTypeMouse;
+  input_data.position.x = kXPos;
+  input_data.position.y = kYPos;
+  input_data.delta.x = delta_x;
+  input_data.delta.y = delta_y;
+  input_data.key_modifiers = key_modifiers;
+
+  SbEvent event;
+  event.type = kSbEventTypeInput;
+  event.data = &input_data;
+
+  DispatchSbEvent(&event);
+}
+
+void EmulateTouch(SbInputEventType type,
+                  float x,
+                  float y,
+                  unsigned int key_modifiers = kSbKeyModifiersNone) {
+  SbInputData input_data = {};
+  input_data.type = type;
+  input_data.device_type = kSbInputDeviceTypeTouchScreen;
+  input_data.position.x = x;
+  input_data.position.y = y;
+  input_data.key_modifiers = key_modifiers;
+  input_data.pressure = kPressure;
+  input_data.size.x = kSizeX;
+  input_data.size.y = kSizeY;
+  input_data.tilt.x = kTiltX;
+  input_data.tilt.y = kTiltY;
 
   SbEvent event;
   event.type = kSbEventTypeInput;
@@ -133,10 +200,10 @@
 // lifetime of the SingleThreadTaskEnvironment.
 class StarboardEventSourceTest : public ::testing::Test {
  protected:
-  StarboardEventSourceTest() : last_ui_event_(nullptr), source_(&delegate_) {
-    ON_CALL(delegate_, DispatchEvent(_)).WillByDefault([&](ui::Event* event) {
-      last_ui_event_ = event->Clone();
-    });
+  StarboardEventSourceTest() : source_(&delegate_) {
+    ON_CALL(delegate_, DispatchEvent(_))
+        .WillByDefault(
+            [this](ui::Event* event) { last_ui_event_ = event->Clone(); });
   }
 
   ~StarboardEventSourceTest() override = default;
@@ -148,7 +215,7 @@
   StarboardEventSource source_;
 };
 
-TEST_F(StarboardEventSourceTest, SupportedKeysArepropagated) {
+TEST_F(StarboardEventSourceTest, SupportedKeysArePropagated) {
   EmulateKey(kSbKeyMediaPlayPause, /*press=*/true);
   ASSERT_NE(last_ui_event_, nullptr);
   EXPECT_TRUE(last_ui_event_->IsKeyEvent());
@@ -157,11 +224,49 @@
   EXPECT_EQ(last_ui_event_->type(), ui::EventType::kKeyPressed);
 
   EmulateKey(kSbKeyMediaPlayPause, /*press=*/false);
+  ASSERT_NE(last_ui_event_, nullptr);
   EXPECT_EQ(last_ui_event_->AsKeyEvent()->code(),
             ui::DomCode::MEDIA_PLAY_PAUSE);
   EXPECT_EQ(last_ui_event_->type(), ui::EventType::kKeyReleased);
 }
 
+TEST_F(StarboardEventSourceTest, LetterKey) {
+  EmulateKey(kSbKeyA, /*press=*/true);
+  ASSERT_NE(last_ui_event_, nullptr);
+  EXPECT_TRUE(last_ui_event_->IsKeyEvent());
+  EXPECT_EQ(last_ui_event_->AsKeyEvent()->code(), ui::DomCode::US_A);
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kKeyPressed);
+  EXPECT_FALSE(last_ui_event_->flags() & ui::EF_SHIFT_DOWN);
+}
+
+TEST_F(StarboardEventSourceTest, LetterKeyWithShift) {
+  EmulateKey(kSbKeyA, /*press=*/true, kSbInputDeviceTypeKeyboard,
+             kSbKeyModifiersShift);
+  ASSERT_NE(last_ui_event_, nullptr);
+  EXPECT_TRUE(last_ui_event_->IsKeyEvent());
+  EXPECT_EQ(last_ui_event_->AsKeyEvent()->code(), ui::DomCode::US_A);
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kKeyPressed);
+  EXPECT_TRUE(last_ui_event_->flags() & ui::EF_SHIFT_DOWN);
+}
+
+TEST_F(StarboardEventSourceTest, DigitKey) {
+  EmulateKey(kSbKey1, /*press=*/true);
+  ASSERT_NE(last_ui_event_, nullptr);
+  EXPECT_EQ(last_ui_event_->AsKeyEvent()->code(), ui::DomCode::DIGIT1);
+}
+
+TEST_F(StarboardEventSourceTest, NumpadKey) {
+  EmulateKey(kSbKeyNumpad5, /*press=*/true);
+  ASSERT_NE(last_ui_event_, nullptr);
+  EXPECT_EQ(last_ui_event_->AsKeyEvent()->code(), ui::DomCode::NUMPAD5);
+}
+
+TEST_F(StarboardEventSourceTest, OemSemicolonKey) {
+  EmulateKey(kSbKeyOem1, /*press=*/true);
+  ASSERT_NE(last_ui_event_, nullptr);
+  EXPECT_EQ(last_ui_event_->AsKeyEvent()->code(), ui::DomCode::SEMICOLON);
+}
+
 TEST_F(StarboardEventSourceTest, UnsupportedKeyIsNotPropagated) {
   EmulateKey(kSbKeyCancel, /*press=*/true);
   EXPECT_EQ(last_ui_event_, nullptr);
@@ -170,46 +275,132 @@
   EXPECT_EQ(last_ui_event_, nullptr);
 }
 
-TEST_F(StarboardEventSourceTest, UnsupportedClickIsNotPropagated) {
-  EmulateKey(kSbKeyMouse2, /*press=*/true);
-  EXPECT_EQ(last_ui_event_, nullptr);
-
-  EmulateKey(kSbKeyMouse2, /*press=*/false);
-  EXPECT_EQ(last_ui_event_, nullptr);
-}
-
-TEST_F(StarboardEventSourceTest, Move) {
-  EmulateMove();
+TEST_F(StarboardEventSourceTest, MouseMove) {
+  EmulateMouseMove(kDeltaX, kDeltaY);
   ASSERT_NE(last_ui_event_, nullptr);
   EXPECT_TRUE(last_ui_event_->IsMouseEvent());
   EXPECT_EQ(last_ui_event_->type(), ui::EventType::kMouseMoved);
-  EXPECT_EQ(last_ui_event_->AsLocatedEvent()->location().x(), kXPosMove);
-  EXPECT_EQ(last_ui_event_->AsLocatedEvent()->location().y(), kYPosMove);
+  EXPECT_EQ(last_ui_event_->AsLocatedEvent()->location_f().x(), kDeltaX);
+  EXPECT_EQ(last_ui_event_->AsLocatedEvent()->location_f().y(), kDeltaY);
+  EXPECT_EQ(last_ui_event_->flags(), 0);
 }
 
-TEST_F(StarboardEventSourceTest, SupportedClick) {
-  EmulateKey(kSbKeyMouse1, true);
+TEST_F(StarboardEventSourceTest, MouseLeftButtonClick) {
+  EmulateKey(kSbKeyMouse1, true, kSbInputDeviceTypeMouse);
   ASSERT_NE(last_ui_event_, nullptr);
   EXPECT_TRUE(last_ui_event_->IsMouseEvent());
   EXPECT_EQ(last_ui_event_->type(), ui::EventType::kMousePressed);
-  EXPECT_EQ(last_ui_event_->AsLocatedEvent()->location().x(), kXPosClick);
-  EXPECT_EQ(last_ui_event_->AsLocatedEvent()->location().y(), kYPosClick);
+  EXPECT_EQ(last_ui_event_->AsLocatedEvent()->location_f().x(), kXPos);
+  EXPECT_EQ(last_ui_event_->AsLocatedEvent()->location_f().y(), kYPos);
   EXPECT_TRUE(last_ui_event_->AsMouseEvent()->IsOnlyLeftMouseButton());
+  EXPECT_TRUE(last_ui_event_->flags() & ui::EF_LEFT_MOUSE_BUTTON);
 
-  EmulateKey(kSbKeyMouse1, false);
+  EmulateKey(kSbKeyMouse1, false, kSbInputDeviceTypeMouse);
+  ASSERT_NE(last_ui_event_, nullptr);
   EXPECT_TRUE(last_ui_event_->IsMouseEvent());
-  EXPECT_EQ(ui::EventType::kMouseReleased, last_ui_event_->type());
-  EXPECT_EQ(kXPosClick, last_ui_event_->AsLocatedEvent()->location().x());
-  EXPECT_EQ(kYPosClick, last_ui_event_->AsLocatedEvent()->location().y());
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kMouseReleased);
   EXPECT_TRUE(last_ui_event_->AsMouseEvent()->IsOnlyLeftMouseButton());
 }
 
-TEST_F(StarboardEventSourceTest, UnsupportedClick) {
-  EmulateKey(kSbKeyMouse2, true);
-  EXPECT_EQ(last_ui_event_, nullptr);
+TEST_F(StarboardEventSourceTest, MouseRightButtonClick) {
+  EmulateKey(kSbKeyMouse2, true, kSbInputDeviceTypeMouse);
+  ASSERT_NE(last_ui_event_, nullptr);
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kMousePressed);
+  EXPECT_TRUE(last_ui_event_->AsMouseEvent()->IsOnlyRightMouseButton());
 
-  EmulateKey(kSbKeyMouse2, false);
-  EXPECT_EQ(last_ui_event_, nullptr);
+  EmulateKey(kSbKeyMouse2, false, kSbInputDeviceTypeMouse);
+  ASSERT_NE(last_ui_event_, nullptr);
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kMouseReleased);
+  EXPECT_TRUE(last_ui_event_->AsMouseEvent()->IsOnlyRightMouseButton());
+}
+
+TEST_F(StarboardEventSourceTest, MouseMiddleButtonClick) {
+  EmulateKey(kSbKeyMouse3, true, kSbInputDeviceTypeMouse);
+  ASSERT_NE(last_ui_event_, nullptr);
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kMousePressed);
+  EXPECT_TRUE(last_ui_event_->AsMouseEvent()->IsOnlyMiddleMouseButton());
+}
+
+TEST_F(StarboardEventSourceTest, MouseDrag) {
+  // Left Mouse + Move creates Drag
+  EmulateKey(kSbKeyMouse1, true, kSbInputDeviceTypeMouse);
+  EmulateMouseMove(kDeltaX, kDeltaY);
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kMouseDragged);
+  EXPECT_TRUE(last_ui_event_->flags() & ui::EF_LEFT_MOUSE_BUTTON);
+
+  // Releasing Left Mouse removes Drag
+  EmulateKey(kSbKeyMouse1, false, kSbInputDeviceTypeMouse);
+  EmulateMouseMove(kDeltaX, kDeltaY);
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kMouseMoved);
+}
+
+TEST_F(StarboardEventSourceTest, MouseDragMulti) {
+  // Left Mouse + Move creates Drag
+  EmulateKey(kSbKeyMouse1, true, kSbInputDeviceTypeMouse);
+  EmulateMouseMove(kDeltaX, kDeltaY);
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kMouseDragged);
+  EXPECT_TRUE(last_ui_event_->flags() & ui::EF_LEFT_MOUSE_BUTTON);
+
+  // Adding Right Mouse creates Drag
+  EmulateKey(kSbKeyMouse2, true, kSbInputDeviceTypeMouse);
+  EmulateMouseMove(kDeltaX, kDeltaY);
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kMouseDragged);
+  EXPECT_TRUE(last_ui_event_->flags() & ui::EF_LEFT_MOUSE_BUTTON);
+  EXPECT_TRUE(last_ui_event_->flags() & ui::EF_RIGHT_MOUSE_BUTTON);
+
+  // Only releasing Left Mouse preserves Drag
+  EmulateKey(kSbKeyMouse1, false, kSbInputDeviceTypeMouse);
+  EmulateMouseMove(kDeltaX, kDeltaY);
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kMouseDragged);
+
+  // Also releasing Right Mouse removes Drag
+  EmulateKey(kSbKeyMouse2, false, kSbInputDeviceTypeMouse);
+  EmulateMouseMove(kDeltaX, kDeltaY);
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kMouseMoved);
+}
+
+TEST_F(StarboardEventSourceTest, MouseWheelScroll) {
+  EmulateMouseWheel(kDeltaX, kDeltaY);
+  ASSERT_NE(last_ui_event_, nullptr);
+  EXPECT_TRUE(last_ui_event_->IsMouseWheelEvent());
+  EXPECT_EQ(last_ui_event_->AsMouseWheelEvent()->x_offset(),
+            static_cast<int>(-kDeltaX * ui::MouseWheelEvent::kWheelDelta));
+  EXPECT_EQ(last_ui_event_->AsMouseWheelEvent()->y_offset(),
+            static_cast<int>(-kDeltaY * ui::MouseWheelEvent::kWheelDelta));
+}
+
+TEST_F(StarboardEventSourceTest, TouchPress) {
+  EmulateTouch(kSbInputEventTypePress, kXPos, kYPos);
+  ASSERT_NE(last_ui_event_, nullptr);
+  EXPECT_TRUE(last_ui_event_->IsTouchEvent());
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kTouchPressed);
+  auto* touch_event = last_ui_event_->AsTouchEvent();
+  EXPECT_EQ(touch_event->location_f().x(), kXPos);
+  EXPECT_EQ(touch_event->location_f().y(), kYPos);
+  EXPECT_EQ(touch_event->pointer_details().id, 0);
+  EXPECT_FLOAT_EQ(touch_event->pointer_details().force, kPressure);
+  EXPECT_FLOAT_EQ(touch_event->pointer_details().radius_x, kSizeX / 2.0f);
+  EXPECT_FLOAT_EQ(touch_event->pointer_details().radius_y, kSizeY / 2.0f);
+  EXPECT_FLOAT_EQ(touch_event->pointer_details().tilt_x, kTiltX);
+  EXPECT_FLOAT_EQ(touch_event->pointer_details().tilt_y, kTiltY);
+}
+
+TEST_F(StarboardEventSourceTest, TouchRelease) {
+  EmulateTouch(kSbInputEventTypeUnpress, kXPos, kYPos);
+  ASSERT_NE(last_ui_event_, nullptr);
+  EXPECT_TRUE(last_ui_event_->IsTouchEvent());
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kTouchReleased);
+  EXPECT_EQ(last_ui_event_->AsTouchEvent()->pointer_details().id, 0);
+}
+
+TEST_F(StarboardEventSourceTest, TouchMove) {
+  EmulateTouch(kSbInputEventTypeMove, kDeltaX, kDeltaY);
+  ASSERT_NE(last_ui_event_, nullptr);
+  EXPECT_TRUE(last_ui_event_->IsTouchEvent());
+  EXPECT_EQ(last_ui_event_->type(), ui::EventType::kTouchMoved);
+  EXPECT_EQ(last_ui_event_->AsTouchEvent()->location_f().x(), kDeltaX);
+  EXPECT_EQ(last_ui_event_->AsTouchEvent()->location_f().y(), kDeltaY);
+  EXPECT_EQ(last_ui_event_->AsTouchEvent()->pointer_details().id, 0);
 }
 
 }  // namespace
diff --git a/chromecast/starboard/media/renderer/BUILD.gn b/chromecast/starboard/media/renderer/BUILD.gn
index fdfdf4e..010070b9 100644
--- a/chromecast/starboard/media/renderer/BUILD.gn
+++ b/chromecast/starboard/media/renderer/BUILD.gn
@@ -46,7 +46,6 @@
     ":geometry_change_handler",
     ":starboard_player_manager",
     "//base",
-    "//chromecast/base/metrics",
     "//chromecast/media/service:video_geometry_setter_service",
     "//chromecast/starboard/media/media:starboard_api_wrapper",
     "//media",
@@ -97,6 +96,7 @@
     "//chromecast/starboard/media/media:starboard_api_wrapper",
     "//media",
   ]
+  deps = [ "//chromecast/base/metrics" ]
 }
 
 source_set("starboard_buffering_tracker") {
diff --git a/chromecast/starboard/media/renderer/client_stats_tracker.cc b/chromecast/starboard/media/renderer/client_stats_tracker.cc
index ac28542..731f104 100644
--- a/chromecast/starboard/media/renderer/client_stats_tracker.cc
+++ b/chromecast/starboard/media/renderer/client_stats_tracker.cc
@@ -6,13 +6,21 @@
 
 #include "base/check.h"
 #include "base/logging.h"
+#include "base/time/time.h"
+#include "chromecast/base/metrics/cast_metrics_helper.h"
 
 namespace chromecast {
 namespace media {
 
-ClientStatsTracker::ClientStatsTracker(::media::RendererClient* client)
-    : client_(client) {
+constexpr base::TimeDelta kStatsUpdateFrequency = base::Seconds(5);
+constexpr int kBitsPerByte = 8;
+
+ClientStatsTracker::ClientStatsTracker(
+    ::media::RendererClient* client,
+    chromecast::metrics::CastMetricsHelper* cast_metrics_helper)
+    : client_(client), cast_metrics_helper_(cast_metrics_helper) {
   CHECK(client_);
+  CHECK(cast_metrics_helper_);
 }
 
 ClientStatsTracker::~ClientStatsTracker() {
@@ -38,10 +46,9 @@
 
   // Per the documentation of RendererClient, *_decoded is a delta when passed
   // to OnStatisticsUpdate.
-  ::media::PipelineStatistics stats;
-  stats.audio_bytes_decoded = sample_info.buffer_size;
+  pending_stats_.audio_bytes_decoded += sample_info.buffer_size;
 
-  client_->OnStatisticsUpdate(stats);
+  MaybeSendStatsUpdate();
 }
 
 void ClientStatsTracker::UpdateVideoStats(
@@ -52,16 +59,60 @@
   // Per the documentation of RendererClient, *_decoded and *_dropped are
   // deltas when passed to OnStatisticsUpdate.
   ::media::PipelineStatistics stats;
-  stats.video_bytes_decoded = sample_info.buffer_size;
-  stats.video_frames_decoded =
-      player_info.total_video_frames - total_video_frames_decoded_;
-  stats.video_frames_dropped =
-      player_info.dropped_video_frames - total_video_frames_dropped_;
+  pending_stats_.video_bytes_decoded += sample_info.buffer_size;
 
-  total_video_frames_decoded_ = player_info.total_video_frames;
-  total_video_frames_dropped_ = player_info.dropped_video_frames;
+  // The pending frame counts should be the delta from the last frame counts we
+  // reported.
+  pending_stats_.video_frames_decoded =
+      player_info.total_video_frames -
+      total_reported_stats_.video_frames_decoded;
+  pending_stats_.video_frames_dropped =
+      player_info.dropped_video_frames -
+      total_reported_stats_.video_frames_dropped;
 
-  client_->OnStatisticsUpdate(stats);
+  MaybeSendStatsUpdate();
+}
+
+void ClientStatsTracker::MaybeSendStatsUpdate() {
+  const base::TimeDelta elapsed_time = last_update_timer_.Elapsed();
+  if (elapsed_time < kStatsUpdateFrequency) {
+    return;
+  }
+
+  client_->OnStatisticsUpdate(pending_stats_);
+
+  const int audio_bitrate_kbps = kBitsPerByte *
+                                 pending_stats_.audio_bytes_decoded /
+                                 elapsed_time.InMilliseconds();
+  const int video_bitrate_kbps = kBitsPerByte *
+                                 pending_stats_.video_bytes_decoded /
+                                 elapsed_time.InMilliseconds();
+
+  if (audio_bitrate_kbps > 0) {
+    LOG(INFO) << "Estimated audio bitrate is " << audio_bitrate_kbps << " kbps";
+    cast_metrics_helper_->RecordApplicationEventWithValue(
+        "Cast.Platform.AudioBitrate", audio_bitrate_kbps);
+  }
+  if (video_bitrate_kbps > 0) {
+    LOG(INFO) << "Estimated video bitrate is " << video_bitrate_kbps << " kbps";
+    cast_metrics_helper_->RecordApplicationEventWithValue(
+        "Cast.Platform.VideoBitrate", video_bitrate_kbps);
+  }
+
+  // Update total_reported_stats_ and reset pending_stats_.
+  total_reported_stats_.video_bytes_decoded +=
+      pending_stats_.video_bytes_decoded;
+  total_reported_stats_.video_frames_decoded +=
+      pending_stats_.video_frames_decoded;
+  total_reported_stats_.video_frames_dropped +=
+      pending_stats_.video_frames_dropped;
+  total_reported_stats_.audio_bytes_decoded +=
+      pending_stats_.audio_bytes_decoded;
+
+  pending_stats_ = ::media::PipelineStatistics();
+
+  // Reset the timer.
+  last_update_timer_ = base::ElapsedTimer();
 }
 
 }  // namespace media
diff --git a/chromecast/starboard/media/renderer/client_stats_tracker.h b/chromecast/starboard/media/renderer/client_stats_tracker.h
index c3b95f7..c8130e8 100644
--- a/chromecast/starboard/media/renderer/client_stats_tracker.h
+++ b/chromecast/starboard/media/renderer/client_stats_tracker.h
@@ -9,19 +9,35 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/sequence_checker.h"
+#include "base/timer/elapsed_timer.h"
 #include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "media/base/pipeline_status.h"
 #include "media/base/renderer_client.h"
 
 namespace chromecast {
+
+namespace metrics {
+class CastMetricsHelper;
+}  // namespace metrics
+
 namespace media {
 
 // Tracks media stats and reports them to a RendererClient.
 //
+// In order to prevent flooding logs, this class is rate limited to once every
+// 5s at most. In other words, if UpdateStats is called, subsequent calls within
+// the next 5s will be ignored (though stats will be tracked internally).
+//
+// 5s was chosen to match the logic of CMA:
+// https://source.chromium.org/chromium/chromium/src/+/main:chromecast/media/cma/pipeline/media_pipeline_impl.cc;l=494;drc=3dd1b27a7cb34cc30ee4d8ddc2146972b5254201
+//
 // This class is not threadsafe, and must only be used on a single sequence.
 class ClientStatsTracker {
  public:
-  // `client` must not be null.
-  explicit ClientStatsTracker(::media::RendererClient* client);
+  // `client` and `cast_metrics_helper` must not be null.
+  explicit ClientStatsTracker(
+      ::media::RendererClient* client,
+      chromecast::metrics::CastMetricsHelper* cast_metrics_helper);
   ~ClientStatsTracker();
 
   // Updates stats based on a buffer pushed to starboard.
@@ -36,11 +52,25 @@
   void UpdateVideoStats(const StarboardPlayerInfo& player_info,
                         const StarboardSampleInfo& sample_info);
 
+  // If enough time has passed, notifies clients of new stats and resets the
+  // timer. Otherwise, this is a no-op.
+  void MaybeSendStatsUpdate();
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   raw_ptr<::media::RendererClient> client_ = nullptr;
-  uint32_t total_video_frames_decoded_ = 0;
-  uint32_t total_video_frames_dropped_ = 0;
+  raw_ptr<chromecast::metrics::CastMetricsHelper> cast_metrics_helper_ =
+      nullptr;
+
+  // These stats track values that we have already reported.
+  ::media::PipelineStatistics total_reported_stats_;
+  // These stats track values that we have not yet reported, due to rate
+  // limiting.
+  ::media::PipelineStatistics pending_stats_;
+
+  // Tracks the amount of time that has elapsed since our last update to client_
+  // and cast_metrics_helper_. Used to rate-limit updates.
+  base::ElapsedTimer last_update_timer_;
 };
 
 }  // namespace media
diff --git a/chromecast/starboard/media/renderer/client_stats_tracker_test.cc b/chromecast/starboard/media/renderer/client_stats_tracker_test.cc
index 0f36350..eaf47a7 100644
--- a/chromecast/starboard/media/renderer/client_stats_tracker_test.cc
+++ b/chromecast/starboard/media/renderer/client_stats_tracker_test.cc
@@ -8,6 +8,9 @@
 #include <cstdint>
 
 #include "base/containers/span.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "chromecast/base/metrics/mock_cast_metrics_helper.h"
 #include "chromecast/starboard/media/media/starboard_api_wrapper.h"
 #include "media/base/mock_filters.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -17,10 +20,14 @@
 namespace media {
 namespace {
 
+using ::base::test::SingleThreadTaskEnvironment;
+using ::chromecast::metrics::MockCastMetricsHelper;
 using ::media::PipelineStatistics;
+using ::testing::_;
 using ::testing::AllOf;
 using ::testing::Eq;
 using ::testing::Field;
+using ::testing::InSequence;
 
 auto MatchesStats(const PipelineStatistics& stats) {
   return AllOf(Field(&PipelineStatistics::audio_bytes_decoded,
@@ -62,6 +69,8 @@
 }
 
 TEST(ClientStatsTrackerTest, UpdatesStatsForAudioBuffer) {
+  SingleThreadTaskEnvironment task_environment{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   constexpr auto kAudioData = std::to_array<uint8_t>({1, 2, 3});
   StarboardPlayerInfo player_info = {};
   StarboardSampleInfo sample_info = CreateAudioSample(kAudioData);
@@ -71,16 +80,20 @@
   expected_stats.video_frames_decoded = 0;
   expected_stats.video_frames_dropped = 0;
 
+  MockCastMetricsHelper metrics_helper;
   ::media::MockRendererClient client;
   EXPECT_CALL(client, OnStatisticsUpdate(MatchesStats(expected_stats)))
       .Times(1);
 
-  ClientStatsTracker stats_tracker(&client);
+  ClientStatsTracker stats_tracker(&client, &metrics_helper);
 
+  task_environment.FastForwardBy(base::Seconds(5));
   stats_tracker.UpdateStats(player_info, sample_info);
 }
 
 TEST(ClientStatsTrackerTest, UpdatesStatsForVideoBuffer) {
+  SingleThreadTaskEnvironment task_environment{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   constexpr auto kVideoData1 = std::to_array<uint8_t>({1, 2, 3, 4, 5});
   constexpr auto kVideoData2 = std::to_array<uint8_t>({6, 7, 8});
 
@@ -106,16 +119,182 @@
   expected_stats_2.video_frames_decoded = 1;
   expected_stats_2.video_frames_dropped = 0;
 
+  MockCastMetricsHelper metrics_helper;
   ::media::MockRendererClient client;
   EXPECT_CALL(client, OnStatisticsUpdate(MatchesStats(expected_stats_1)))
       .Times(1);
   EXPECT_CALL(client, OnStatisticsUpdate(MatchesStats(expected_stats_2)))
       .Times(1);
 
-  ClientStatsTracker stats_tracker(&client);
+  ClientStatsTracker stats_tracker(&client, &metrics_helper);
+
+  task_environment.FastForwardBy(base::Seconds(5));
+  stats_tracker.UpdateStats(player_info_1, sample_info_1);
+
+  task_environment.FastForwardBy(base::Seconds(5));
+  stats_tracker.UpdateStats(player_info_2, sample_info_2);
+}
+
+TEST(ClientStatsTrackerTest, DoesNotUpdateStatsIfNotEnoughTimeHasPassed) {
+  SingleThreadTaskEnvironment task_environment{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  constexpr auto kAudioData = std::to_array<uint8_t>({1, 2, 3});
+  StarboardPlayerInfo player_info = {};
+  StarboardSampleInfo sample_info = CreateAudioSample(kAudioData);
+  PipelineStatistics expected_stats;
+  expected_stats.audio_bytes_decoded = kAudioData.size();
+  expected_stats.video_bytes_decoded = 0;
+  expected_stats.video_frames_decoded = 0;
+  expected_stats.video_frames_dropped = 0;
+
+  MockCastMetricsHelper metrics_helper;
+  ::media::MockRendererClient client;
+  EXPECT_CALL(client, OnStatisticsUpdate(_)).Times(0);
+
+  ClientStatsTracker stats_tracker(&client, &metrics_helper);
+
+  // No time has elapsed between the creation of stats_tracker and the call to
+  // UpdateStats.
+  stats_tracker.UpdateStats(player_info, sample_info);
+}
+
+TEST(ClientStatsTrackerTest, AccumulatesStatsFromMultipleBuffers) {
+  SingleThreadTaskEnvironment task_environment{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  // The size of the buffer is all that matters here.
+  const std::vector<uint8_t> video_data_1(10000);
+  const std::vector<uint8_t> video_data_2(5000);
+  const std::vector<uint8_t> audio_data(5000);
+
+  StarboardPlayerInfo player_info_1 = {};
+  player_info_1.total_video_frames = 3;
+  player_info_1.dropped_video_frames = 1;
+
+  StarboardPlayerInfo player_info_2 = {};
+  player_info_2.total_video_frames = 5;
+  player_info_2.dropped_video_frames = 1;
+
+  // Not relevant to audio stats.
+  StarboardPlayerInfo player_info_3 = {};
+
+  StarboardSampleInfo sample_info_1 = CreateVideoSample(video_data_1);
+  StarboardSampleInfo sample_info_2 = CreateVideoSample(video_data_2);
+  StarboardSampleInfo sample_info_3 = CreateAudioSample(audio_data);
+
+  PipelineStatistics expected_stats;
+  expected_stats.audio_bytes_decoded = audio_data.size();
+  expected_stats.video_bytes_decoded =
+      video_data_1.size() + video_data_2.size();
+  expected_stats.video_frames_decoded = 5;
+  expected_stats.video_frames_dropped = 1;
+
+  MockCastMetricsHelper metrics_helper;
+  EXPECT_CALL(metrics_helper,
+              RecordApplicationEventWithValue("Cast.Platform.AudioBitrate", 8));
+  EXPECT_CALL(metrics_helper, RecordApplicationEventWithValue(
+                                  "Cast.Platform.VideoBitrate", 24));
+  ::media::MockRendererClient client;
+  EXPECT_CALL(client, OnStatisticsUpdate(MatchesStats(expected_stats)))
+      .Times(1);
+
+  ClientStatsTracker stats_tracker(&client, &metrics_helper);
 
   stats_tracker.UpdateStats(player_info_1, sample_info_1);
   stats_tracker.UpdateStats(player_info_2, sample_info_2);
+
+  task_environment.FastForwardBy(base::Seconds(5));
+  // Now that enough time has passed, the stats should be updated with info from
+  // all 3 buffers.
+  stats_tracker.UpdateStats(player_info_3, sample_info_3);
+}
+
+TEST(ClientStatsTrackerTest, UpdatesAreDeltasAndNotCumulative) {
+  // This test verifies that ClientStatsTracker resets its stats after each
+  // update to clients.
+  SingleThreadTaskEnvironment task_environment{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+  // The size of the buffer is all that matters here.
+  const std::vector<uint8_t> video_data_1(10000);
+
+  // These buffers will be pushed after the first update.
+  const std::vector<uint8_t> video_data_2(5000);
+  const std::vector<uint8_t> audio_data(5000);
+
+  StarboardPlayerInfo player_info_1 = {};
+  player_info_1.total_video_frames = 3;
+  player_info_1.dropped_video_frames = 1;
+
+  StarboardPlayerInfo player_info_2 = {};
+  player_info_2.total_video_frames = 5;
+  player_info_2.dropped_video_frames = 1;
+
+  // Not relevant to audio stats.
+  StarboardPlayerInfo player_info_3 = {};
+
+  StarboardSampleInfo sample_info_1 = CreateVideoSample(video_data_1);
+  StarboardSampleInfo sample_info_2 = CreateVideoSample(video_data_2);
+  StarboardSampleInfo sample_info_3 = CreateAudioSample(audio_data);
+
+  // stats for just sample_info_1 and player_info_1.
+  PipelineStatistics expected_stats_1;
+  expected_stats_1.audio_bytes_decoded = 0;
+  expected_stats_1.video_bytes_decoded = video_data_1.size();
+  expected_stats_1.video_frames_decoded = 3;
+  expected_stats_1.video_frames_dropped = 1;
+
+  // stats for sample_info_2, player_info_2, sample_info_3, and player_info_3.
+  // Note that these stats should be deltas, not totals.
+  PipelineStatistics expected_stats_2;
+  expected_stats_2.audio_bytes_decoded = audio_data.size();
+  expected_stats_2.video_bytes_decoded = video_data_2.size();
+  expected_stats_2.video_frames_decoded = 2;
+  expected_stats_2.video_frames_dropped = 0;
+
+  MockCastMetricsHelper metrics_helper;
+
+  // The ordering of audio and video stats updates is irrelevant.
+  EXPECT_CALL(metrics_helper,
+              RecordApplicationEventWithValue("Cast.Platform.AudioBitrate", 4))
+      .Times(1);
+
+  {
+    InSequence seq;
+    EXPECT_CALL(metrics_helper, RecordApplicationEventWithValue(
+                                    "Cast.Platform.VideoBitrate", 16))
+        .Times(1);
+    EXPECT_CALL(metrics_helper, RecordApplicationEventWithValue(
+                                    "Cast.Platform.VideoBitrate", 4))
+        .Times(1);
+  }
+
+  ::media::MockRendererClient client;
+  {
+    InSequence seq;
+    EXPECT_CALL(client, OnStatisticsUpdate(MatchesStats(expected_stats_1)))
+        .Times(1);
+    EXPECT_CALL(client, OnStatisticsUpdate(MatchesStats(expected_stats_2)))
+        .Times(1);
+  }
+
+  ClientStatsTracker stats_tracker(&client, &metrics_helper);
+
+  task_environment.FastForwardBy(base::Seconds(5));
+  // Enough time has passed, so the stats should be sent to clients.
+  stats_tracker.UpdateStats(player_info_1, sample_info_1);
+
+  task_environment.FastForwardBy(base::Seconds(1));
+  // This update does not get immediately pushed, since not enough time has
+  // passed.
+  stats_tracker.UpdateStats(player_info_2, sample_info_2);
+
+  // Note that the delta is 10s total here. This affects the bitrate that gets
+  // reported to the metrics helper.
+  task_environment.FastForwardBy(base::Seconds(9));
+
+  // Now that enough time has passed, stats for the second and third buffer
+  // should be pushed.
+  stats_tracker.UpdateStats(player_info_3, sample_info_3);
 }
 
 }  // namespace
diff --git a/chromecast/starboard/media/renderer/starboard_player_manager.cc b/chromecast/starboard/media/renderer/starboard_player_manager.cc
index de7a56f..2f1c286 100644
--- a/chromecast/starboard/media/renderer/starboard_player_manager.cc
+++ b/chromecast/starboard/media/renderer/starboard_player_manager.cc
@@ -164,7 +164,7 @@
       drm_resource_(std::move(drm_resource)),
       starboard_(starboard),
       client_(client),
-      stats_tracker_(client),
+      stats_tracker_(client, cast_metrics_helper),
       task_runner_(std::move(media_task_runner)),
       demuxer_stream_reader_(
           audio_stream,
@@ -177,10 +177,12 @@
           base::BindRepeating(&StarboardPlayerManager::PushEos,
                               base::Unretained(this)),
           client_,
-          cast_metrics_helper) {
+          cast_metrics_helper),
+      cast_metrics_helper_(cast_metrics_helper) {
   CHECK(starboard_);
   CHECK(client_);
   CHECK(task_runner_);
+  CHECK(cast_metrics_helper_);
   // player_ is set later in the factory function to create
   // StarboardPlayerManager.
 }
@@ -381,7 +383,23 @@
   DCHECK_EQ(player, player_);
   LOG(ERROR) << "Received SbPlayer error " << error
              << ", with message: " << message;
-  client_->OnError(::media::PIPELINE_ERROR_COULD_NOT_RENDER);
+
+  // PIPELINE_ERROR_HARDWARE_CONTEXT_RESET roughly corresponds to Starboard's
+  // kSbPlayerErrorCapabilityChanged. It signifies that the system should
+  // recreate the renderer instead of just giving up (since capabilities
+  // changed, e.g. due to a hardware change like unplugging an external GPU).
+  //
+  // See
+  // https://source.chromium.org/chromium/chromium/src/+/main:media/base/pipeline_impl.cc;l=986;drc=1fb4c56b03b105b03c45627871b15b8933ed8a11
+  // and
+  // https://github.com/youtube/cobalt/blob/6a2df0d123c68a3a29555dedf25fdbc5d161b3c9/starboard/player.h#L69
+  const ::media::PipelineStatusCodes pipeline_error =
+      error == StarboardPlayerError::kStarboardPlayerErrorDecode
+          ? ::media::PIPELINE_ERROR_DECODE
+          : ::media::PIPELINE_ERROR_HARDWARE_CONTEXT_RESET;
+  cast_metrics_helper_->RecordApplicationEventWithValue("Cast.Platform.Error",
+                                                        pipeline_error);
+  client_->OnError(pipeline_error);
 }
 
 void StarboardPlayerManager::CallOnDecoderStatus(
diff --git a/chromecast/starboard/media/renderer/starboard_player_manager.h b/chromecast/starboard/media/renderer/starboard_player_manager.h
index 90071df..927dd2a 100644
--- a/chromecast/starboard/media/renderer/starboard_player_manager.h
+++ b/chromecast/starboard/media/renderer/starboard_player_manager.h
@@ -184,6 +184,8 @@
   base::flat_map<raw_ptr<const void>, scoped_refptr<::media::DecoderBuffer>>
       addr_to_buffer_;
   std::optional<StarboardBufferingTracker> buffering_tracker_;
+  raw_ptr<chromecast::metrics::CastMetricsHelper> cast_metrics_helper_ =
+      nullptr;
 
   // This should be destructed first, to invalidate any weak ptrs.
   base::WeakPtrFactory<StarboardPlayerManager> weak_factory_{this};
diff --git a/chromecast/starboard/media/renderer/starboard_player_manager_test.cc b/chromecast/starboard/media/renderer/starboard_player_manager_test.cc
index 5c7e3ea..a007fb2d 100644
--- a/chromecast/starboard/media/renderer/starboard_player_manager_test.cc
+++ b/chromecast/starboard/media/renderer/starboard_player_manager_test.cc
@@ -22,6 +22,7 @@
 #include "media/base/demuxer_stream.h"
 #include "media/base/encryption_scheme.h"
 #include "media/base/mock_filters.h"
+#include "media/base/test_helpers.h"
 #include "media/base/video_color_space.h"
 #include "media/base/video_transformation.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -1136,6 +1137,97 @@
   RunPendingTasks();
 }
 
+TEST_F(StarboardPlayerManagerTest, ReportsDecodeErrorToClientAndMetricsHelper) {
+  // This will be set to the callbacks received by the mock Starboard.
+  const StarboardPlayerCallbackHandler* callbacks = nullptr;
+  EXPECT_CALL(
+      starboard_,
+      CreatePlayer(
+          Pointee(MatchesPlayerCreationParam(StarboardPlayerCreationParam{
+              .drm_system = nullptr,
+              .audio_sample_info = GetStarboardAudioConfig(),
+              .video_sample_info = GetStarboardVideoConfig(),
+              .output_mode = StarboardPlayerOutputMode::
+                  kStarboardPlayerOutputModePunchOut})),
+          _))
+      .WillOnce(DoAll(SaveArg<1>(&callbacks), Return(&sb_player_)));
+
+  EXPECT_CALL(renderer_client_,
+              OnError(HasStatusCode(::media::PIPELINE_ERROR_DECODE)));
+  // Ignore any unrelated metrics calls.
+  EXPECT_CALL(metrics_helper_, RecordApplicationEventWithValue(_, _))
+      .Times(AnyNumber());
+  // The StarboardPlayerManager should report a cast platform error to the
+  // metrics helper.
+  EXPECT_CALL(metrics_helper_,
+              RecordApplicationEventWithValue("Cast.Platform.Error",
+                                              ::media::PIPELINE_ERROR_DECODE));
+
+  audio_stream_.set_audio_decoder_config(GetChromiumAudioConfig());
+  video_stream_.set_video_decoder_config(GetChromiumVideoConfig());
+
+  auto player_manager = StarboardPlayerManager::Create(
+      &starboard_, &audio_stream_, &video_stream_, &renderer_client_,
+      &metrics_helper_, base::SequencedTaskRunner::GetCurrentDefault(),
+      /*enable_buffering=*/true);
+  ASSERT_THAT(player_manager, NotNull());
+
+  // Simulate a decode error from Starboard.
+  ASSERT_THAT(callbacks, NotNull());
+  ASSERT_THAT(callbacks->player_error_fn, NotNull());
+  ASSERT_THAT(callbacks->context, NotNull());
+  callbacks->player_error_fn(&sb_player_, callbacks->context,
+                             StarboardPlayerError::kStarboardPlayerErrorDecode,
+                             "");
+}
+
+TEST_F(StarboardPlayerManagerTest,
+       ReportsCapabilitiesErrorToClientAndMetricsHelper) {
+  // This will be set to the callbacks received by the mock Starboard.
+  const StarboardPlayerCallbackHandler* callbacks = nullptr;
+  EXPECT_CALL(
+      starboard_,
+      CreatePlayer(
+          Pointee(MatchesPlayerCreationParam(StarboardPlayerCreationParam{
+              .drm_system = nullptr,
+              .audio_sample_info = GetStarboardAudioConfig(),
+              .video_sample_info = GetStarboardVideoConfig(),
+              .output_mode = StarboardPlayerOutputMode::
+                  kStarboardPlayerOutputModePunchOut})),
+          _))
+      .WillOnce(DoAll(SaveArg<1>(&callbacks), Return(&sb_player_)));
+
+  EXPECT_CALL(
+      renderer_client_,
+      OnError(HasStatusCode(::media::PIPELINE_ERROR_HARDWARE_CONTEXT_RESET)));
+  // Ignore any unrelated metrics calls.
+  EXPECT_CALL(metrics_helper_, RecordApplicationEventWithValue(_, _))
+      .Times(AnyNumber());
+  // The StarboardPlayerManager should report a cast platform error to the
+  // metrics helper.
+  EXPECT_CALL(metrics_helper_,
+              RecordApplicationEventWithValue(
+                  "Cast.Platform.Error",
+                  ::media::PIPELINE_ERROR_HARDWARE_CONTEXT_RESET));
+
+  audio_stream_.set_audio_decoder_config(GetChromiumAudioConfig());
+  video_stream_.set_video_decoder_config(GetChromiumVideoConfig());
+
+  auto player_manager = StarboardPlayerManager::Create(
+      &starboard_, &audio_stream_, &video_stream_, &renderer_client_,
+      &metrics_helper_, base::SequencedTaskRunner::GetCurrentDefault(),
+      /*enable_buffering=*/true);
+  ASSERT_THAT(player_manager, NotNull());
+
+  // Simulate a capabilities change from Starboard.
+  ASSERT_THAT(callbacks, NotNull());
+  ASSERT_THAT(callbacks->player_error_fn, NotNull());
+  ASSERT_THAT(callbacks->context, NotNull());
+  callbacks->player_error_fn(
+      &sb_player_, callbacks->context,
+      StarboardPlayerError::kStarboardPlayerErrorCapabilityChanged, "");
+}
+
 }  // namespace
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromecast/starboard/media/renderer/starboard_renderer.cc b/chromecast/starboard/media/renderer/starboard_renderer.cc
index 1efd1aa..f456c8b6 100644
--- a/chromecast/starboard/media/renderer/starboard_renderer.cc
+++ b/chromecast/starboard/media/renderer/starboard_renderer.cc
@@ -25,21 +25,29 @@
     scoped_refptr<base::SequencedTaskRunner> media_task_runner,
     const base::UnguessableToken& overlay_plane_id,
     bool enable_buffering,
-    VideoGeometrySetterService* geometry_setter_service)
+    VideoGeometrySetterService* geometry_setter_service,
+    chromecast::metrics::CastMetricsHelper* cast_metrics_helper)
     : starboard_(std::move(starboard)),
       media_task_runner_(std::move(media_task_runner)),
       geometry_change_handler_(geometry_setter_service,
                                starboard_.get(),
                                overlay_plane_id),
+      cast_metrics_helper_(cast_metrics_helper),
       enable_buffering_(enable_buffering) {
   CHECK(starboard_);
   CHECK(media_task_runner_);
+  CHECK(cast_metrics_helper_);
   LOG(INFO) << "Constructed StarboardRenderer. Buffering is "
             << (enable_buffering_ ? "enabled" : "disabled");
 }
 
 StarboardRenderer::~StarboardRenderer() {
   CHECK(media_task_runner_->RunsTasksInCurrentSequence());
+  // player_manager_ being non-null implies that StarboardRenderer::Initialize
+  // was called.
+  if (player_manager_ && !end_reported_) {
+    cast_metrics_helper_->RecordApplicationEvent("Cast.Platform.Ended");
+  }
 }
 
 void StarboardRenderer::Initialize(::media::MediaResource* media_resource,
@@ -55,8 +63,7 @@
 
   player_manager_ = StarboardPlayerManager::Create(
       starboard_.get(), audio_stream, video_stream, client,
-      chromecast::metrics::CastMetricsHelper::GetInstance(), media_task_runner_,
-      enable_buffering_);
+      cast_metrics_helper_, media_task_runner_, enable_buffering_);
 
   if (!player_manager_) {
     LOG(ERROR) << "Unable to create StarboardPlayerManager";
@@ -105,16 +112,30 @@
   CHECK(media_task_runner_->RunsTasksInCurrentSequence());
   player_manager_->Flush();
   std::move(flush_cb).Run();
+  cast_metrics_helper_->RecordApplicationEvent("Cast.Platform.Ended");
+  end_reported_ = true;
 }
 
 void StarboardRenderer::StartPlayingFrom(base::TimeDelta time) {
   CHECK(media_task_runner_->RunsTasksInCurrentSequence());
+  LOG(INFO) << "StartPlayingFrom t=" << time;
   player_manager_->StartPlayingFrom(time);
+  cast_metrics_helper_->RecordApplicationEvent("Cast.Platform.Playing");
+
+  end_reported_ = false;
 }
 
 void StarboardRenderer::SetPlaybackRate(double playback_rate) {
   CHECK(media_task_runner_->RunsTasksInCurrentSequence());
   player_manager_->SetPlaybackRate(playback_rate);
+
+  if (playback_rate == 0.0f) {
+    cast_metrics_helper_->RecordApplicationEvent("Cast.Platform.Pause");
+  } else {
+    cast_metrics_helper_->RecordApplicationEvent("Cast.Platform.Playing");
+  }
+
+  end_reported_ = false;
 }
 
 void StarboardRenderer::SetVolume(float volume) {
diff --git a/chromecast/starboard/media/renderer/starboard_renderer.h b/chromecast/starboard/media/renderer/starboard_renderer.h
index 7f537224..bc8423e9 100644
--- a/chromecast/starboard/media/renderer/starboard_renderer.h
+++ b/chromecast/starboard/media/renderer/starboard_renderer.h
@@ -9,6 +9,7 @@
 #include <optional>
 
 #include "base/functional/callback.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/task/sequenced_task_runner.h"
@@ -22,6 +23,11 @@
 #include "media/base/renderer_client.h"
 
 namespace chromecast {
+
+namespace metrics {
+class CastMetricsHelper;
+}  // namespace metrics
+
 namespace media {
 
 class VideoGeometrySetterService;
@@ -33,11 +39,13 @@
 // (media_task_runner, passed into the constructor).
 class StarboardRenderer : public ::media::Renderer {
  public:
-  StarboardRenderer(std::unique_ptr<StarboardApiWrapper> starboard,
-                    scoped_refptr<base::SequencedTaskRunner> media_task_runner,
-                    const base::UnguessableToken& overlay_plane_id,
-                    bool enable_buffering,
-                    VideoGeometrySetterService* geometry_setter_service);
+  StarboardRenderer(
+      std::unique_ptr<StarboardApiWrapper> starboard,
+      scoped_refptr<base::SequencedTaskRunner> media_task_runner,
+      const base::UnguessableToken& overlay_plane_id,
+      bool enable_buffering,
+      VideoGeometrySetterService* geometry_setter_service,
+      chromecast::metrics::CastMetricsHelper* cast_metrics_helper);
 
   // Disallow copy and assign.
   StarboardRenderer(const StarboardRenderer&) = delete;
@@ -69,11 +77,16 @@
 
   std::unique_ptr<StarboardApiWrapper> starboard_;
   scoped_refptr<base::SequencedTaskRunner> media_task_runner_;
-  ::media::RendererClient* client_ = nullptr;
+  raw_ptr<::media::RendererClient> client_ = nullptr;
   std::unique_ptr<StarboardPlayerManager> player_manager_;
   // This must be destructed before starboard_.
   GeometryChangeHandler geometry_change_handler_;
+  raw_ptr<chromecast::metrics::CastMetricsHelper> cast_metrics_helper_ =
+      nullptr;
   bool enable_buffering_ = true;
+  // Whether a Cast.Platform.Ended message has already been reported for this
+  // play. Used to avoid double reporting the Cast.Platform.Ended metric.
+  bool end_reported_ = false;
 
   // This is set if a volume change is made before SbPlayer is created.
   std::optional<float> pending_volume_;
diff --git a/chromecast/starboard/media/renderer/starboard_renderer_test.cc b/chromecast/starboard/media/renderer/starboard_renderer_test.cc
index 9564f164..5eedbbc 100644
--- a/chromecast/starboard/media/renderer/starboard_renderer_test.cc
+++ b/chromecast/starboard/media/renderer/starboard_renderer_test.cc
@@ -27,6 +27,7 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "base/unguessable_token.h"
+#include "chromecast/base/metrics/mock_cast_metrics_helper.h"
 #include "chromecast/media/service/mojom/video_geometry_setter.mojom.h"
 #include "chromecast/media/service/video_geometry_setter_service.h"
 #include "chromecast/starboard/media/cdm/starboard_drm_key_tracker.h"
@@ -56,6 +57,7 @@
 using ::base::test::RunOnceCallback;
 using ::media::DemuxerStream;
 using ::testing::_;
+using ::testing::AnyNumber;
 using ::testing::AtLeast;
 using ::testing::DoAll;
 using ::testing::DoubleEq;
@@ -278,6 +280,7 @@
   NiceMock<::media::MockDemuxerStream> video_stream_ =
       NiceMock<::media::MockDemuxerStream>(DemuxerStream::Type::VIDEO);
   NiceMock<::media::MockRendererClient> client_;
+  NiceMock<chromecast::metrics::MockCastMetricsHelper> cast_metrics_helper_;
   MockFunction<void(::media::PipelineStatus)> pipeline_status_fn_;
   std::unique_ptr<NiceMock<MockStarboardApiWrapper>> starboard_ =
       std::make_unique<NiceMock<MockStarboardApiWrapper>>();
@@ -442,7 +445,7 @@
 
   StarboardRenderer renderer(std::move(starboard_), task_runner_, plane_id_,
                              /*enable_buffering=*/true,
-                             &geometry_setter_service_);
+                             &geometry_setter_service_, &cast_metrics_helper_);
 
   EXPECT_CALL(pipeline_status_fn_,
               Call(HasStatusCode(::media::PipelineStatusCodes::PIPELINE_OK)))
@@ -517,9 +520,21 @@
                 kMediaTime.InMicroseconds();
           }));
 
+  // Ignore unrelated metrics calls.
+  EXPECT_CALL(cast_metrics_helper_, RecordApplicationEvent(_))
+      .Times(AnyNumber());
+  // Should be called at flush, but not at destruction.
+  EXPECT_CALL(cast_metrics_helper_,
+              RecordApplicationEvent("Cast.Platform.Ended"))
+      .Times(1);
+  // Should be called when setting the playback rate to a nonzero value.
+  EXPECT_CALL(cast_metrics_helper_,
+              RecordApplicationEvent("Cast.Platform.Playing"))
+      .Times(AtLeast(1));
+
   StarboardRenderer renderer(std::move(starboard_), task_runner_, plane_id_,
                              /*enable_buffering=*/true,
-                             &geometry_setter_service_);
+                             &geometry_setter_service_, &cast_metrics_helper_);
 
   EXPECT_CALL(pipeline_status_fn_,
               Call(HasStatusCode(::media::PipelineStatusCodes::PIPELINE_OK)))
@@ -546,7 +561,7 @@
 
   StarboardRenderer renderer(std::move(starboard_), task_runner_, plane_id_,
                              /*enable_buffering=*/true,
-                             &geometry_setter_service_);
+                             &geometry_setter_service_, &cast_metrics_helper_);
 
   EXPECT_CALL(pipeline_status_fn_,
               Call(HasStatusCode(::media::PipelineStatusCodes::PIPELINE_OK)))
@@ -587,7 +602,7 @@
 
   StarboardRenderer renderer(std::move(starboard_), task_runner_, plane_id_,
                              /*enable_buffering=*/true,
-                             &geometry_setter_service_);
+                             &geometry_setter_service_, &cast_metrics_helper_);
   RunPendingTasks();
 
   static_cast<mojom::VideoGeometrySetter*>(&geometry_setter_service_)
@@ -621,7 +636,7 @@
 
   StarboardRenderer renderer(std::move(starboard_), task_runner_, plane_id_,
                              /*enable_buffering=*/true,
-                             &geometry_setter_service_);
+                             &geometry_setter_service_, &cast_metrics_helper_);
 
   EXPECT_CALL(pipeline_status_fn_,
               Call(HasStatusCode(::media::PipelineStatusCodes::PIPELINE_OK)))
@@ -634,6 +649,36 @@
   EXPECT_EQ(renderer.GetMediaTime(), kMediaTime);
 }
 
+TEST_F(StarboardRendererTest, SetPlaybackRateReportsMetric) {
+  // Ignore unrelated metrics calls.
+  EXPECT_CALL(cast_metrics_helper_, RecordApplicationEvent(_))
+      .Times(AnyNumber());
+  // Should be called at destruction.
+  EXPECT_CALL(cast_metrics_helper_,
+              RecordApplicationEvent("Cast.Platform.Ended"))
+      .Times(1);
+  // Should be called when setting the playback rate to a nonzero value.
+  EXPECT_CALL(cast_metrics_helper_,
+              RecordApplicationEvent("Cast.Platform.Playing"))
+      .Times(1);
+  // Should be called when setting the playback rate to 0.
+  EXPECT_CALL(cast_metrics_helper_,
+              RecordApplicationEvent("Cast.Platform.Pause"))
+      .Times(1);
+
+  StarboardRenderer renderer(std::move(starboard_), task_runner_, plane_id_,
+                             /*enable_buffering=*/true,
+                             &geometry_setter_service_, &cast_metrics_helper_);
+
+  renderer.Initialize(
+      &media_resource_, &client_,
+      base::BindLambdaForTesting(pipeline_status_fn_.AsStdFunction()));
+  RunPendingTasks();
+
+  renderer.SetPlaybackRate(1.0);
+  renderer.SetPlaybackRate(0.0);
+}
+
 }  // namespace
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index d10452f8..9457ae7f 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-16439.0.0-1072191
\ No newline at end of file
+16441.0.0-1072235
\ No newline at end of file
diff --git a/chromeos/ash/components/quick_start/quick_start_metrics.cc b/chromeos/ash/components/quick_start/quick_start_metrics.cc
index 4cce57d..9af5504 100644
--- a/chromeos/ash/components/quick_start/quick_start_metrics.cc
+++ b/chromeos/ash/components/quick_start/quick_start_metrics.cc
@@ -442,17 +442,16 @@
 }
 
 void QuickStartMetrics::RecordAttestationCertificateRequested() {
-  CHECK(!attestation_certificate_timer_)
-      << "Only 1 attestation certificate request can be active at a time";
+  // Timer may already exist if the user entered and cancelled Quick Start.
+  if (attestation_certificate_timer_) {
+    attestation_certificate_timer_.reset();
+  }
+
   attestation_certificate_timer_ = std::make_unique<base::ElapsedTimer>();
 }
 
 void QuickStartMetrics::RecordAttestationCertificateRequestEnded(
     std::optional<AttestationCertificateRequestErrorCode> error_code) {
-  CHECK(attestation_certificate_timer_)
-      << "Attestation certificate request timer was not active. Unexpected "
-         "response.";
-
   if (error_code) {
     base::UmaHistogramEnumeration(
         kAttestationCertificateFailureReasonHistogramName, error_code.value());
@@ -463,9 +462,11 @@
                               true);
   }
 
-  base::UmaHistogramTimes(kAttestationCertificateFetchDurationHistogramName,
-                          attestation_certificate_timer_->Elapsed());
-  attestation_certificate_timer_.reset();
+  if (attestation_certificate_timer_) {
+    base::UmaHistogramTimes(kAttestationCertificateFetchDurationHistogramName,
+                            attestation_certificate_timer_->Elapsed());
+    attestation_certificate_timer_.reset();
+  }
 }
 
 void QuickStartMetrics::RecordGaiaAuthenticationStarted() {
diff --git a/chromeos/ash/experiences/arc/session/arc_bridge_host_impl.cc b/chromeos/ash/experiences/arc/session/arc_bridge_host_impl.cc
index fef30c9..be6c509 100644
--- a/chromeos/ash/experiences/arc/session/arc_bridge_host_impl.cc
+++ b/chromeos/ash/experiences/arc/session/arc_bridge_host_impl.cc
@@ -304,7 +304,7 @@
 void ArcBridgeHostImpl::OnNotificationsInstanceReady(
     mojo::PendingRemote<mojom::NotificationsInstance> notifications_remote) {
   auto* host_initializer = ash::ArcNotificationsHostInitializer::Get();
-  auto* manager = host_initializer->GetArcNotificationManagerInstance();
+  auto* manager = host_initializer->GetArcNotificationManager();
   if (manager) {
     static_cast<ash::ArcNotificationManager*>(manager)->SetInstance(
         std::move(notifications_remote));
@@ -313,8 +313,8 @@
   // Forward notification instance to ash by injecting ArcNotificationManager.
   auto new_manager = std::make_unique<ash::ArcNotificationManager>();
   new_manager->SetInstance(std::move(notifications_remote));
-  ash::ArcNotificationsHostInitializer::Get()
-      ->SetArcNotificationManagerInstance(std::move(new_manager));
+  ash::ArcNotificationsHostInitializer::Get()->SetArcNotificationManager(
+      std::move(new_manager));
 }
 
 void ArcBridgeHostImpl::OnObbMounterInstanceReady(
diff --git a/chromeos/ash/services/recording/audio_capture_test_base.cc b/chromeos/ash/services/recording/audio_capture_test_base.cc
index b8fce57..a6d432e 100644
--- a/chromeos/ash/services/recording/audio_capture_test_base.cc
+++ b/chromeos/ash/services/recording/audio_capture_test_base.cc
@@ -4,7 +4,7 @@
 
 #include "chromeos/ash/services/recording/audio_capture_test_base.h"
 
-#include "base/compiler_specific.h"
+#include "base/types/zip.h"
 #include "chromeos/ash/services/recording/audio_capture_util.h"
 
 namespace recording {
@@ -27,13 +27,10 @@
     return false;
   }
 
-  for (int i = 0; i < bus1.channels(); ++i) {
-    const auto* const bus1_channel = bus1.channel(i);
-    const auto* const bus2_channel = bus2.channel(i);
-    for (int j = 0; j < bus1.frames(); ++j) {
-      if (UNSAFE_TODO(bus1_channel[j]) != UNSAFE_TODO(bus2_channel[j])) {
-        return false;
-      }
+  for (const auto [bus1_ch, bus2_ch] :
+       base::zip(bus1.AllChannels(), bus2.AllChannels())) {
+    if (bus1_ch != bus2_ch) {
+      return false;
     }
   }
 
diff --git a/chromeos/ash/services/recording/audio_stream_mixer_unittest.cc b/chromeos/ash/services/recording/audio_stream_mixer_unittest.cc
index 1334216..ef6c1da 100644
--- a/chromeos/ash/services/recording/audio_stream_mixer_unittest.cc
+++ b/chromeos/ash/services/recording/audio_stream_mixer_unittest.cc
@@ -7,9 +7,9 @@
 #include <algorithm>
 #include <memory>
 
-#include "base/compiler_specific.h"
 #include "base/functional/bind.h"
 #include "base/time/time.h"
+#include "base/types/zip.h"
 #include "chromeos/ash/services/recording/audio_capture_test_base.h"
 #include "chromeos/ash/services/recording/audio_capture_util.h"
 #include "chromeos/ash/services/recording/audio_stream.h"
@@ -29,13 +29,10 @@
   DCHECK_EQ(bus1.frames(), bus2.frames());
 
   auto output_bus = media::AudioBus::Create(bus1.channels(), bus1.frames());
-  for (int i = 0; i < output_bus->channels(); ++i) {
-    const auto* const bus1_channel = bus1.channel(i);
-    const auto* const bus2_channel = bus2.channel(i);
-    auto* const output_bus_channel = output_bus->channel(i);
-    for (int j = 0; j < bus1.frames(); ++j) {
-      UNSAFE_TODO(output_bus_channel[j]) =
-          UNSAFE_TODO(bus1_channel[j]) + UNSAFE_TODO(bus2_channel[j]);
+  for (auto [src1_ch, src2_ch, dest_ch] : base::zip(
+           bus1.AllChannels(), bus2.AllChannels(), output_bus->AllChannels())) {
+    for (int i = 0; i < bus1.frames(); ++i) {
+      dest_ch[i] = src1_ch[i] + src2_ch[i];
     }
   }
 
diff --git a/chromeos/ash/services/recording/audio_stream_unittest.cc b/chromeos/ash/services/recording/audio_stream_unittest.cc
index 7efcb72e..71262b1 100644
--- a/chromeos/ash/services/recording/audio_stream_unittest.cc
+++ b/chromeos/ash/services/recording/audio_stream_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "chromeos/ash/services/recording/audio_stream.h"
 
-#include "base/compiler_specific.h"
 #include "base/memory/aligned_memory.h"
 #include "base/time/time.h"
 #include "chromeos/ash/services/recording/audio_capture_test_base.h"
@@ -210,9 +209,8 @@
   int mis_aligned_start_frame = 0;
   for (; mis_aligned_start_frame < destination->frames();
        ++mis_aligned_start_frame) {
-    if (!base::IsAligned(
-            &UNSAFE_TODO(destination->channel(0)[mis_aligned_start_frame]),
-            media::vector_math::kRequiredAlignment)) {
+    if (!base::IsAligned(&destination->channel_span(0)[mis_aligned_start_frame],
+                         media::vector_math::kRequiredAlignment)) {
       break;
     }
   }
diff --git a/chromeos/crosapi/mojom/video_conference.mojom b/chromeos/crosapi/mojom/video_conference.mojom
index 6e101a4..de885a1 100644
--- a/chromeos/crosapi/mojom/video_conference.mojom
+++ b/chromeos/crosapi/mojom/video_conference.mojom
@@ -138,9 +138,9 @@
   // Opens and focuses media app corresponding to the |id|.
   ReturnToApp@1(mojo_base.mojom.UnguessableToken id) => (bool success);
 
-  // Informs client of system device's disable status
+  // Informs client of system device's enable status
   SetSystemMediaDeviceStatus@2(VideoConferenceMediaDevice device,
-                               bool disabled) => (bool success);
+                               bool enabled) => (bool success);
 
   // Stops screen sharings from all media apps.
   [MinVersion=1]
diff --git a/chromeos/services/tts/tts_player.cc b/chromeos/services/tts/tts_player.cc
index 7ce2164..1c12180 100644
--- a/chromeos/services/tts/tts_player.cc
+++ b/chromeos/services/tts/tts_player.cc
@@ -4,7 +4,6 @@
 
 #include "chromeos/services/tts/tts_player.h"
 
-#include "base/compiler_specific.h"
 #include "base/task/single_thread_task_runner.h"
 
 namespace chromeos {
@@ -67,7 +66,7 @@
     if (buffers_.empty())
       return 0;
 
-    float* channel = dest->channel(0);
+    auto channel = dest->channel_span(0);
 
     AudioBuffer* buffer = &buffers_.front();
     for (size_t output_index = 0; output_index < frame_count;
@@ -81,8 +80,7 @@
         }
         buffer = &buffers_.front();
       }
-      UNSAFE_TODO(channel[output_index]) =
-          buffer->frames[buffer->current_frame_index];
+      channel[output_index] = buffer->frames[buffer->current_frame_index];
     }
 
     CHECK(!buffer->frames.empty());
diff --git a/chromeos/services/tts/tts_player_unittest.cc b/chromeos/services/tts/tts_player_unittest.cc
index f95ee61..985a246 100644
--- a/chromeos/services/tts/tts_player_unittest.cc
+++ b/chromeos/services/tts/tts_player_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "chromeos/services/tts/tts_player.h"
 
-#include "base/compiler_specific.h"
 #include "chromeos/services/tts/constants.h"
 #include "chromeos/services/tts/tts_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -104,9 +103,9 @@
   EXPECT_EQ(1, backing_observer_.end_count);
 
   EXPECT_EQ(1, frames_rendered);
-  std::vector<float> actual(bus->channel(0), UNSAFE_TODO(bus->channel(0) + 1));
-  std::vector<float> expected = {0.7};
-  EXPECT_THAT(actual, testing::ElementsAreArray(expected));
+  auto actual = bus->channel_span(0).first<1u>();
+  constexpr std::array<float, 1> kExpected = {0.7};
+  EXPECT_EQ(actual, base::span(kExpected));
 }
 
 TEST_F(TtsPlayerTest, RenderFramesFromPartialBuffers) {
@@ -135,9 +134,9 @@
   EXPECT_EQ(1, backing_observer_.end_count);
 
   EXPECT_EQ(5, frames_rendered);
-  std::vector<float> actual(bus->channel(0), UNSAFE_TODO(bus->channel(0) + 5));
-  std::vector<float> expected = {0.1, 0.2, 0.3, 0.4, 0.5};
-  EXPECT_THAT(actual, testing::ElementsAreArray(expected));
+  auto actual = bus->channel_span(0).first<5u>();
+  constexpr std::array<float, 5> kExpected = {0.1, 0.2, 0.3, 0.4, 0.5};
+  EXPECT_EQ(actual, base::span(kExpected));
 }
 
 TEST_F(TtsPlayerTest, RenderBusWithFramesFromEmptyAndPartialBuffers) {
@@ -176,9 +175,9 @@
   EXPECT_EQ(0, backing_observer_.end_count);
 
   EXPECT_EQ(5, frames_rendered);
-  std::vector<float> actual(bus->channel(0), UNSAFE_TODO(bus->channel(0) + 5));
-  std::vector<float> expected = {0.1, 0.2, 0.3, 0.4, 0.5};
-  EXPECT_THAT(actual, testing::ElementsAreArray(expected));
+  auto actual = bus->channel_span(0).first<5u>();
+  constexpr std::array<float, 5> kExpected = {0.1, 0.2, 0.3, 0.4, 0.5};
+  EXPECT_EQ(actual, base::span(kExpected));
 }
 
 TEST_F(TtsPlayerTest, RenderMultiBusFromMultiBuffers) {
@@ -214,10 +213,9 @@
   EXPECT_EQ(0, backing_observer_.end_count);
 
   EXPECT_EQ(5, frames_rendered);
-  std::vector<float> actual(first_bus->channel(0),
-                            UNSAFE_TODO(first_bus->channel(0) + 5));
-  std::vector<float> expected = {0.1, 0.2, 0.3, 0.4, 0.5};
-  EXPECT_THAT(actual, testing::ElementsAreArray(expected));
+  auto first_actual = first_bus->channel_span(0).first<5u>();
+  constexpr std::array<float, 5> kFirstExpected = {0.1, 0.2, 0.3, 0.4, 0.5};
+  EXPECT_EQ(first_actual, base::span(kFirstExpected));
 
   // Render two frames to second bus.
   auto second_bus = media::AudioBus::Create(/*channels=*/1, /*frames=*/5);
@@ -232,10 +230,9 @@
   EXPECT_EQ(1, backing_observer_.end_count);
 
   EXPECT_EQ(2, frames_rendered);
-  actual = std::vector<float>(second_bus->channel(0),
-                              UNSAFE_TODO(second_bus->channel(0) + 2));
-  expected = {0.6, 0.7};
-  EXPECT_THAT(actual, testing::ElementsAreArray(expected));
+  auto second_actual = second_bus->channel_span(0).first<2u>();
+  constexpr std::array<float, 2> kSecondExpected = {0.6, 0.7};
+  EXPECT_THAT(second_actual, base::span(kSecondExpected));
 }
 
 }  // namespace
diff --git a/chromeos/ui/frame/frame_view_chromeos_interactive_uitest.cc b/chromeos/ui/frame/frame_view_chromeos_interactive_uitest.cc
index 0e43f7dc..fd3beb39 100644
--- a/chromeos/ui/frame/frame_view_chromeos_interactive_uitest.cc
+++ b/chromeos/ui/frame/frame_view_chromeos_interactive_uitest.cc
@@ -95,9 +95,6 @@
 
 IN_PROC_BROWSER_TEST_F(FrameViewChromeOSUiTest,
                        TabletModeTitlebarHideForSnappedWindow) {
-  if (!IsSnapWindowSupported()) {
-    GTEST_SKIP() << "Ash is too old.";
-  }
   // TODO(https://crbug.com/325001477) Lacros updates inactive windows' state
   // late on tablet state change, so we have to wait for activation before
   // entering tablet mode so that there isn't a delay.
diff --git a/clank b/clank
index 42a6e48..7cb2794 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 42a6e482e94535fc8270814c8092516fc9996892
+Subproject commit 7cb2794b10723ee11ae31558250443ca39feeb79
diff --git a/components/autofill/android/BUILD.gn b/components/autofill/android/BUILD.gn
index 4e35cac..98fe700a 100644
--- a/components/autofill/android/BUILD.gn
+++ b/components/autofill/android/BUILD.gn
@@ -309,6 +309,7 @@
     "java/src/org/chromium/components/autofill/payments/AutofillSaveCardUiInfo.java",
     "java/src/org/chromium/components/autofill/payments/AutofillSaveIbanUiInfo.java",
     "java/src/org/chromium/components/autofill/payments/BankAccount.java",
+    "java/src/org/chromium/components/autofill/payments/BnplIssuerTosDetail.java",
     "java/src/org/chromium/components/autofill/payments/CardDetail.java",
     "java/src/org/chromium/components/autofill/payments/Ewallet.java",
     "java/src/org/chromium/components/autofill/payments/LegalMessageLine.java",
@@ -332,6 +333,7 @@
     "javatests/src/org/chromium/components/autofill/payments/AutofillSaveCardUiInfoTest.java",
     "javatests/src/org/chromium/components/autofill/payments/AutofillSaveIbanUiInfoTest.java",
     "javatests/src/org/chromium/components/autofill/payments/BankAccountTest.java",
+    "javatests/src/org/chromium/components/autofill/payments/BnplIssuerTosDetailTest.java",
     "javatests/src/org/chromium/components/autofill/payments/CardDetailTest.java",
     "javatests/src/org/chromium/components/autofill/payments/EwalletTest.java",
     "javatests/src/org/chromium/components/autofill/payments/LegalMessageLineTest.java",
diff --git a/components/autofill/android/java/src/org/chromium/components/autofill/payments/BnplIssuerTosDetail.java b/components/autofill/android/java/src/org/chromium/components/autofill/payments/BnplIssuerTosDetail.java
new file mode 100644
index 0000000..261c1f2
--- /dev/null
+++ b/components/autofill/android/java/src/org/chromium/components/autofill/payments/BnplIssuerTosDetail.java
@@ -0,0 +1,47 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.autofill.payments;
+
+import android.text.SpannableString;
+
+import org.chromium.build.annotations.NullMarked;
+
+/** Detailed BNPL issuer ToS to show in the BNPL issuer ToS bottonsheet. */
+@NullMarked
+public class BnplIssuerTosDetail {
+    /** Sign-in/create account message. */
+    private final String mReviewText;
+
+    /** Eligibility check message. */
+    private final String mApproveText;
+
+    /** Account link/unlink message. */
+    private final SpannableString mLinkText;
+
+    /**
+     * Creates a new instance of the detailed card information.
+     *
+     * @param reviewText String for sign-in/create account message.
+     * @param approveText String for eligibility check message.
+     * @param linkText String for account link/unlink message.
+     */
+    public BnplIssuerTosDetail(String reviewText, String approveText, SpannableString linkText) {
+        mReviewText = reviewText;
+        mApproveText = approveText;
+        mLinkText = linkText;
+    }
+
+    public String getReviewText() {
+        return mReviewText;
+    }
+
+    public String getApproveText() {
+        return mApproveText;
+    }
+
+    public SpannableString getLinkText() {
+        return mLinkText;
+    }
+}
diff --git a/components/autofill/android/javatests/src/org/chromium/components/autofill/payments/BnplIssuerTosDetailTest.java b/components/autofill/android/javatests/src/org/chromium/components/autofill/payments/BnplIssuerTosDetailTest.java
new file mode 100644
index 0000000..9581e2c7
--- /dev/null
+++ b/components/autofill/android/javatests/src/org/chromium/components/autofill/payments/BnplIssuerTosDetailTest.java
@@ -0,0 +1,29 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.autofill.payments;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+
+import android.text.SpannableString;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+@RunWith(BaseRobolectricTestRunner.class)
+public class BnplIssuerTosDetailTest {
+    @Test
+    public void bnplIssuerTosDetail_constructor_setsProperties() {
+        BnplIssuerTosDetail bnplIssuerTosDetail =
+                new BnplIssuerTosDetail(
+                        "review text", "approve text", new SpannableString("link text"));
+
+        assertThat(bnplIssuerTosDetail.getReviewText(), equalTo("review text"));
+        assertThat(bnplIssuerTosDetail.getApproveText(), equalTo("approve text"));
+        assertThat(bnplIssuerTosDetail.getLinkText().toString(), equalTo("link text"));
+    }
+}
diff --git a/components/autofill/content/browser/content_autofill_client.cc b/components/autofill/content/browser/content_autofill_client.cc
index ed8c5442..e0f975f 100644
--- a/components/autofill/content/browser/content_autofill_client.cc
+++ b/components/autofill/content/browser/content_autofill_client.cc
@@ -17,6 +17,10 @@
   return autofill_driver_factory_;
 }
 
+bool ContentAutofillClient::DocumentUsedWebOTP() {
+  return GetWebContents().GetPrimaryMainFrame()->DocumentUsedWebOTP();
+}
+
 WEB_CONTENTS_USER_DATA_KEY_IMPL(ContentAutofillClient);
 
 }  // namespace autofill
diff --git a/components/autofill/content/browser/content_autofill_client.h b/components/autofill/content/browser/content_autofill_client.h
index 893ba46b..dde8d8fa 100644
--- a/components/autofill/content/browser/content_autofill_client.h
+++ b/components/autofill/content/browser/content_autofill_client.h
@@ -44,6 +44,9 @@
   virtual credential_management::ContentCredentialManager*
   GetContentCredentialManager() = 0;
 
+  // Implementation of AutofillClient:
+  bool DocumentUsedWebOTP() final;
+
  private:
   friend class content::WebContentsUserData<ContentAutofillClient>;
 
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index cfe7a65..e492cb2 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -222,8 +222,7 @@
   if (url.ProtocolIs("about") && GURL(url).IsAboutSrcdoc()) {
     return true;
   }
-  return !base::FeatureList::IsEnabled(
-      features::kAutofillExtractOnlyOnAdmissibleUrls);
+  return false;
 }
 
 // Returns the form's |name| attribute if non-empty; otherwise the form's |id|
@@ -1953,9 +1952,6 @@
     field->set_nonce(GetAttribute<kNonce>(element).Utf16());
   }
 
-  const bool kAutofillDetectFieldVisibilityEnabled =
-      base::FeatureList::IsEnabled(features::kAutofillDetectFieldVisibility);
-
   // Traverse up through shadow hosts to see if we can gather missing
   // attributes.
   // TODO(crbug.com/40204601): Make sure this works for all shadow DOM cases,
@@ -2007,9 +2003,7 @@
   field->set_is_autofilled(element.IsAutofilled());
   field->set_is_user_edited(element.UserHasEditedTheField());
   field->set_is_focusable(element.IsFocusable());
-  field->set_is_visible(kAutofillDetectFieldVisibilityEnabled
-                            ? IsWebElementVisible(element)
-                            : field->is_focusable());
+  field->set_is_visible(IsWebElementVisible(element));
   field->set_should_autocomplete(
       element.AutoComplete() &&
       !(field->parsed_autocomplete().has_value() &&
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 778d9f8..ce06910 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -769,6 +769,8 @@
     "webdata/account_settings/account_setting_service.h",
     "webdata/account_settings/account_setting_sync_bridge.cc",
     "webdata/account_settings/account_setting_sync_bridge.h",
+    "webdata/account_settings/account_setting_sync_util.cc",
+    "webdata/account_settings/account_setting_sync_util.h",
     "webdata/addresses/address_autofill_table.cc",
     "webdata/addresses/address_autofill_table.h",
     "webdata/addresses/autofill_profile_sync_bridge.cc",
@@ -1290,6 +1292,7 @@
     "ui/test_autofill_external_delegate.h",
     "ui/test_autofill_image_fetcher.cc",
     "ui/test_autofill_image_fetcher.h",
+    "webdata/account_settings/account_setting_sync_test_util.h",
     "webdata/autofill_ai/entity_table_test_api.h",
     "webdata/autofill_webdata_service_test_helper.cc",
     "webdata/autofill_webdata_service_test_helper.h",
@@ -1642,6 +1645,8 @@
     "ui/payments/select_bnpl_issuer_dialog_controller_impl_unittest.cc",
     "ui/payments/virtual_card_enroll_ui_model_unittest.cc",
     "ui/region_combobox_model_unittest.cc",
+    "webdata/account_settings/account_setting_service_unittest.cc",
+    "webdata/account_settings/account_setting_sync_bridge_unittest.cc",
     "webdata/addresses/address_autofill_table_unittest.cc",
     "webdata/addresses/autofill_profile_sync_bridge_unittest.cc",
     "webdata/addresses/autofill_profile_sync_difference_tracker_unittest.cc",
diff --git a/components/autofill/core/browser/autofill_field.cc b/components/autofill/core/browser/autofill_field.cc
index 21e6c9ab..2b8abee 100644
--- a/components/autofill/core/browser/autofill_field.cc
+++ b/components/autofill/core/browser/autofill_field.cc
@@ -629,9 +629,7 @@
   // Indicates whether `ft` may be part of the union type.
   auto is_union_type_candidate = [](FieldType ft) {
     return GroupTypeOfFieldType(ft) == FieldTypeGroup::kAutofillAi &&
-           base::FeatureList::IsEnabled(features::kAutofillAiWithDataSchema) &&
-           base::FeatureList::IsEnabled(
-               features::kAutofillUnionTypesForAutofillAi);
+           base::FeatureList::IsEnabled(features::kAutofillAiWithDataSchema);
   };
 
   // Returns the union of
diff --git a/components/autofill/core/browser/autofill_field_unittest.cc b/components/autofill/core/browser/autofill_field_unittest.cc
index 7113b24c0..93eecb01 100644
--- a/components/autofill/core/browser/autofill_field_unittest.cc
+++ b/components/autofill/core/browser/autofill_field_unittest.cc
@@ -187,63 +187,37 @@
   EXPECT_THAT(f(NAME_FIRST, USERNAME), ElementsAre(NAME_FIRST));
   EXPECT_THAT(f(USERNAME, NAME_FIRST), ElementsAre(USERNAME));
 
-  {
-    // If kAutofillUnionTypesForAutofillAi is disabled, the Autofill AI
-    // predictions do not affect the overall type.
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitAndDisableFeature(
-        features::kAutofillUnionTypesForAutofillAi);
-    EXPECT_THAT(f(ADDRESS_HOME_COUNTRY, PASSPORT_ISSUING_COUNTRY),
-                UnorderedElementsAre(ADDRESS_HOME_COUNTRY));
-    EXPECT_THAT(f(PASSPORT_ISSUING_COUNTRY, ADDRESS_HOME_COUNTRY),
-                UnorderedElementsAre(PASSPORT_ISSUING_COUNTRY));
-    EXPECT_THAT(
-        f(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_REGION, VEHICLE_PLATE_STATE),
-        UnorderedElementsAre(ADDRESS_HOME_COUNTRY));
-    EXPECT_THAT(f(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_NUMBER,
-                  DRIVERS_LICENSE_REGION, VEHICLE_LICENSE_PLATE),
-                UnorderedElementsAre(ADDRESS_HOME_COUNTRY));
-    EXPECT_THAT(
-        f(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_NUMBER, DRIVERS_LICENSE_REGION,
-          VEHICLE_LICENSE_PLATE, VEHICLE_PLATE_STATE),
-        UnorderedElementsAre(ADDRESS_HOME_COUNTRY));
-  }
+  base::test::ScopedFeatureList feature_list(
+      features::kAutofillAiWithDataSchema);
 
-  {
-    // If kAutofillUnionTypesForAutofillAi is enabled, the Autofill AI
-    // predictions are part of the overall type.
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitWithFeatures({features::kAutofillAiWithDataSchema,
-                                   features::kAutofillUnionTypesForAutofillAi},
-                                  {});
-    EXPECT_THAT(
-        f(ADDRESS_HOME_COUNTRY, PASSPORT_ISSUING_COUNTRY),
-        UnorderedElementsAre(ADDRESS_HOME_COUNTRY, PASSPORT_ISSUING_COUNTRY));
-    EXPECT_THAT(f(PASSPORT_ISSUING_COUNTRY, ADDRESS_HOME_COUNTRY),
-                UnorderedElementsAre(PASSPORT_ISSUING_COUNTRY));
-    // Multiple Autofill AI predictions may coexist.
-    EXPECT_THAT(
-        f(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_REGION, VEHICLE_PLATE_STATE),
-        UnorderedElementsAre(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_REGION,
-                             VEHICLE_PLATE_STATE));
-    // Conflict resolution: when there are multiple predictions from the same
-    // entities, we take the longest prefix that satisfies the AutofillType
-    // constraints.
-    EXPECT_THAT(f(NAME_FULL, DRIVERS_LICENSE_NUMBER),
-                UnorderedElementsAre(NAME_FULL));
-    EXPECT_THAT(
-        f(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_NUMBER, DRIVERS_LICENSE_REGION,
-          VEHICLE_LICENSE_PLATE),
-        UnorderedElementsAre(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_NUMBER));
-    EXPECT_THAT(
-        f(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_NUMBER, DRIVERS_LICENSE_REGION,
-          VEHICLE_LICENSE_PLATE, VEHICLE_PLATE_STATE),
-        UnorderedElementsAre(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_NUMBER));
-    EXPECT_THAT(
-        f(ADDRESS_HOME_COUNTRY, ADDRESS_HOME_STATE, DRIVERS_LICENSE_NUMBER,
-          DRIVERS_LICENSE_REGION, VEHICLE_LICENSE_PLATE, VEHICLE_PLATE_STATE),
-        UnorderedElementsAre(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_NUMBER));
-  }
+  // The Autofill AI predictions are part of the overall type.
+  EXPECT_THAT(
+      f(ADDRESS_HOME_COUNTRY, PASSPORT_ISSUING_COUNTRY),
+      UnorderedElementsAre(ADDRESS_HOME_COUNTRY, PASSPORT_ISSUING_COUNTRY));
+  EXPECT_THAT(f(PASSPORT_ISSUING_COUNTRY, ADDRESS_HOME_COUNTRY),
+              UnorderedElementsAre(PASSPORT_ISSUING_COUNTRY));
+  // Multiple Autofill AI predictions may coexist.
+  EXPECT_THAT(
+      f(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_REGION, VEHICLE_PLATE_STATE),
+      UnorderedElementsAre(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_REGION,
+                           VEHICLE_PLATE_STATE));
+  // Conflict resolution: when there are multiple predictions from the same
+  // entities, we take the longest prefix that satisfies the AutofillType
+  // constraints.
+  EXPECT_THAT(f(NAME_FULL, DRIVERS_LICENSE_NUMBER),
+              UnorderedElementsAre(NAME_FULL));
+  EXPECT_THAT(
+      f(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_NUMBER, DRIVERS_LICENSE_REGION,
+        VEHICLE_LICENSE_PLATE),
+      UnorderedElementsAre(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_NUMBER));
+  EXPECT_THAT(
+      f(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_NUMBER, DRIVERS_LICENSE_REGION,
+        VEHICLE_LICENSE_PLATE, VEHICLE_PLATE_STATE),
+      UnorderedElementsAre(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_NUMBER));
+  EXPECT_THAT(
+      f(ADDRESS_HOME_COUNTRY, ADDRESS_HOME_STATE, DRIVERS_LICENSE_NUMBER,
+        DRIVERS_LICENSE_REGION, VEHICLE_LICENSE_PLATE, VEHICLE_PLATE_STATE),
+      UnorderedElementsAre(ADDRESS_HOME_COUNTRY, DRIVERS_LICENSE_NUMBER));
 }
 
 // Tests that if a heuristic type is set, additional server types may influence
diff --git a/components/autofill/core/browser/data_manager/addresses/account_name_email_store.cc b/components/autofill/core/browser/data_manager/addresses/account_name_email_store.cc
index bde8f5d..63ab28e2 100644
--- a/components/autofill/core/browser/data_manager/addresses/account_name_email_store.cc
+++ b/components/autofill/core/browser/data_manager/addresses/account_name_email_store.cc
@@ -15,6 +15,7 @@
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/sync/base/features.h"
 #include "components/sync/base/user_selectable_type.h"
 #include "components/sync/service/sync_service.h"
 #include "components/sync/service/sync_user_settings.h"
@@ -41,8 +42,6 @@
     syncer::SyncService& sync_service,
     PrefService& pref_service)
     : address_data_manager_(address_data_manager),
-      identity_manager_(identity_manager),
-      sync_service_(sync_service),
       pref_service_(pref_service) {
   identity_manager_observer_.Observe(&identity_manager);
   sync_service_observer_.Observe(&sync_service);
@@ -58,8 +57,12 @@
 
 void AccountNameEmailStore::OnExtendedAccountInfoUpdated(
     const AccountInfo& info) {
+  if (!identity_manager_observer_.GetSource()) {
+    return;
+  }
   const std::optional<CoreAccountInfo>& primary_info =
-      identity_manager_->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);
+      identity_manager_observer_.GetSource()->GetPrimaryAccountInfo(
+          signin::ConsentLevel::kSignin);
   if (!primary_info.has_value() ||
       (!primary_info->IsEmpty() && info.gaia != primary_info->gaia)) {
     return;
@@ -67,10 +70,13 @@
   MaybeUpdateOrCreateAccountNameEmail();
 }
 
+void AccountNameEmailStore::OnIdentityManagerShutdown(
+    signin::IdentityManager*) {
+  identity_manager_observer_.Reset();
+}
+
 void AccountNameEmailStore::OnSyncShutdown(syncer::SyncService*) {
-  // Unreachable, since the service owning this instance is Shutdown() before
-  // the SyncService.
-  NOTREACHED();
+  sync_service_observer_.Reset();
 }
 
 void AccountNameEmailStore::OnStateChanged(syncer::SyncService* sync_service) {
@@ -108,18 +114,21 @@
 }
 
 void AccountNameEmailStore::MaybeUpdateOrCreateAccountNameEmail() {
-  if (GetBlockAccountNameEmailUpdateReason().has_value()) {
+  if (!identity_manager_observer_.GetSource() ||
+      GetBlockAccountNameEmailUpdateReason().has_value()) {
     return;
   }
 
   const std::optional<CoreAccountInfo>& core_info =
-      identity_manager_->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);
+      identity_manager_observer_.GetSource()->GetPrimaryAccountInfo(
+          signin::ConsentLevel::kSignin);
   if (!core_info.has_value()) {
     return;
   }
 
   const std::optional<AccountInfo>& extended_info =
-      identity_manager_->FindExtendedAccountInfo(core_info.value());
+      identity_manager_observer_.GetSource()->FindExtendedAccountInfo(
+          core_info.value());
   if (!extended_info.has_value()) {
     return;
   }
@@ -224,17 +233,25 @@
 
 std::optional<AccountNameEmailStore::ProfileUpdateBlockReason>
 AccountNameEmailStore::GetBlockAccountNameEmailUpdateReason() {
-  if (sync_service_->GetTransportState() ==
+  if (!sync_service_observer_.GetSource()) {
+    return ProfileUpdateBlockReason::kSyncDisabled;
+  }
+
+  if (sync_service_observer_.GetSource()->GetTransportState() ==
       syncer::SyncService::TransportState::DISABLED) {
     return ProfileUpdateBlockReason::kUserSignedOut;
   }
 
-  if (!sync_service_->IsSyncFeatureActive()) {
+  if (!base::FeatureList::IsEnabled(
+          syncer::kReplaceSyncPromosWithSignInPromos) &&
+      !sync_service_observer_.GetSource()->IsSyncFeatureEnabled()) {
     return ProfileUpdateBlockReason::kSyncDisabled;
   }
 
-  if (!sync_service_->GetUserSettings()->GetSelectedTypes().Has(
-          syncer::UserSelectableType::kAutofill)) {
+  if (!sync_service_observer_.GetSource()
+           ->GetUserSettings()
+           ->GetSelectedTypes()
+           .Has(syncer::UserSelectableType::kAutofill)) {
     return ProfileUpdateBlockReason::kAutofillSyncToggleDisabled;
   }
 
@@ -242,7 +259,7 @@
     return ProfileUpdateBlockReason::kDataNotLoaded;
   }
 
-  switch (sync_service_->GetDownloadStatusFor(
+  switch (sync_service_observer_.GetSource()->GetDownloadStatusFor(
       syncer::DataType::PRIORITY_PREFERENCES)) {
     case syncer::SyncService::DataTypeDownloadStatus::kWaitingForUpdates:
       return ProfileUpdateBlockReason::kDataNotLoaded;
diff --git a/components/autofill/core/browser/data_manager/addresses/account_name_email_store.h b/components/autofill/core/browser/data_manager/addresses/account_name_email_store.h
index 2833be1..12bdc62 100644
--- a/components/autofill/core/browser/data_manager/addresses/account_name_email_store.h
+++ b/components/autofill/core/browser/data_manager/addresses/account_name_email_store.h
@@ -62,6 +62,8 @@
   // Called when the account's extended information (e.g. full name) is
   // updated. Used to keep the kAccountNameEmail profile up to date.
   void OnExtendedAccountInfoUpdated(const AccountInfo& info) override;
+  void OnIdentityManagerShutdown(
+      signin::IdentityManager* identity_manager) override;
 
   // syncer::SyncServiceObserver:
   void OnSyncShutdown(syncer::SyncService* sync) override;
@@ -118,9 +120,9 @@
   // profile will be removed.
   void OnCounterPrefUpdated();
 
+  // `this` is owned by `address_data_manager_`, so `address_data_manager_` will
+  // outlive this class.
   const raw_ref<AddressDataManager> address_data_manager_;
-  const raw_ref<signin::IdentityManager> identity_manager_;
-  const raw_ref<syncer::SyncService> sync_service_;
   raw_ref<PrefService> pref_service_;
 
   // Used to update the `kAccountNameEmail` profile when the account name
diff --git a/components/autofill/core/browser/data_manager/addresses/account_name_email_store_unittest.cc b/components/autofill/core/browser/data_manager/addresses/account_name_email_store_unittest.cc
index 63f8592..df91721 100644
--- a/components/autofill/core/browser/data_manager/addresses/account_name_email_store_unittest.cc
+++ b/components/autofill/core/browser/data_manager/addresses/account_name_email_store_unittest.cc
@@ -23,6 +23,7 @@
 #include "components/signin/public/identity_manager/accounts_mutator.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "components/sync/base/features.h"
 #include "components/sync/test/test_sync_service.h"
 #include "google_apis/gaia/gaia_id.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -44,9 +45,9 @@
 constexpr std::string_view kTestEmailAddress2 = "thomas.jefferson@gmail.com";
 constexpr GaiaId::Literal kFakeGaiaId("1234567890");
 
-class AccountNameEmailStoreTest : public testing::Test {
+class AccountNameEmailStoreCoreTest : public testing::Test {
  public:
-  AccountNameEmailStoreTest()
+  AccountNameEmailStoreCoreTest()
       : prefs_(test::PrefServiceForTesting()),
         identity_manager_(identity_test_env_.identity_manager()),
         store_(test_adm_, *identity_manager_, sync_service_, *prefs_) {}
@@ -114,9 +115,10 @@
   }
   syncer::TestSyncService& sync_service() { return sync_service_; }
 
+ protected:
+  base::test::ScopedFeatureList features_;
+
  private:
-  base::test::ScopedFeatureList feature_{
-      features::kAutofillEnableSupportForNameAndEmail};
   base::test::TaskEnvironment task_environment_;
   std::unique_ptr<PrefService> prefs_;
   signin::IdentityTestEnvironment identity_test_env_;
@@ -136,6 +138,14 @@
          arg->GetAddressCountryCode().value().empty();
 }
 
+class AccountNameEmailStoreTest : public AccountNameEmailStoreCoreTest {
+ public:
+  AccountNameEmailStoreTest() {
+    features_.InitWithFeatures(
+        {features::kAutofillEnableSupportForNameAndEmail}, {});
+  }
+};
+
 // Tests that a new `kAccountNameEmail` profile isn't created when an empty
 // `AccountInfo` is passed into the `MaybeUpdateOrCreateAccountNameEmail`
 // method.
@@ -593,10 +603,19 @@
                                             base::UTF8ToUTF16(info.email))));
 }
 
+class AccountNameEmailStoreSyncTest : public AccountNameEmailStoreCoreTest {
+ public:
+  AccountNameEmailStoreSyncTest() {
+    features_.InitWithFeatures(
+        {features::kAutofillEnableSupportForNameAndEmail},
+        {syncer::kReplaceSyncPromosWithSignInPromos});
+  }
+};
+
 // Tests that kAccountNameEmail profile exists only if sync-the-feature is
 // enabled.
 // TODO(crbug.com/40066949): Remove once kSync gets removed.
-TEST_F(AccountNameEmailStoreTest, SyncTheFeatureState) {
+TEST_F(AccountNameEmailStoreSyncTest, SyncTheFeatureState) {
   sync_service().SetSignedIn(signin::ConsentLevel::kSignin);
   sync_service().FireStateChanged();
 
diff --git a/components/autofill/core/browser/filling/form_filler_unittest.cc b/components/autofill/core/browser/filling/form_filler_unittest.cc
index a41709a9..2571a7b 100644
--- a/components/autofill/core/browser/filling/form_filler_unittest.cc
+++ b/components/autofill/core/browser/filling/form_filler_unittest.cc
@@ -1547,10 +1547,8 @@
 }
 
 TEST_F(FormFillerTest, FillPassportEntity) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatures({features::kAutofillAiWithDataSchema,
-                                 features::kAutofillUnionTypesForAutofillAi},
-                                {});
+  base::test::ScopedFeatureList feature_list(
+      features::kAutofillAiWithDataSchema);
   FormData form = test::GetFormData({.fields = {
                                          // Passport number:
                                          {.role = UNKNOWN_TYPE},
diff --git a/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types_unittest.cc b/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types_unittest.cc
index 4a9f6b5..bea584ce 100644
--- a/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types_unittest.cc
+++ b/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types_unittest.cc
@@ -79,15 +79,10 @@
  public:
   static constexpr DetermineAttributeTypesPassKey kPassKey = {};
 
-  DetermineAttributeTypesTest() {
-    feature_list_.InitWithFeatures({features::kAutofillAiWithDataSchema,
-                                    features::kAutofillUnionTypesForAutofillAi},
-                                   {});
-  }
-
  private:
   autofill::test::AutofillUnitTestEnvironment autofill_environment_;
-  base::test::ScopedFeatureList feature_list_;
+  base::test::ScopedFeatureList feature_list_{
+      features::kAutofillAiWithDataSchema};
 };
 
 // Tests that DetermineAttributeTypes() doesn't crash on empty lists.
@@ -410,8 +405,6 @@
 // of AutofillField::Type(). We test it here nonetheless because it is an
 // important property of DetermineAttributeTypes().
 TEST_F(DetermineAttributeTypesTest, AtMostOneAttributePerFieldPerEntity) {
-  base::test::ScopedFeatureList feature_list(
-      features::kAutofillUnionTypesForAutofillAi);
   using enum AttributeTypeName;
 
   std::vector<std::unique_ptr<AutofillField>> fields = CreateFields(
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index 9ac6804..1fb25d6 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -182,31 +182,24 @@
 void FormStructure::RationalizeAndAssignSections(
     const GeoIpCountryCode& client_country,
     const LanguageCode& current_page_language,
-    LogManager* log_manager,
-    bool legacy_order) {
-  if (base::FeatureList::IsEnabled(
-          features::kAutofillUnifyRationalizationAndSectioningOrder)) {
-    // We call AssignSections() before *and* after rationalization because
-    // - rationalization depends on sections and
-    // - sectioning depends on field types, which rationalization may change.
-    AssignSections(fields_);
-    // TODO(crbug.com/408497919): Merge the two Rationalize*() functions when
-    // kAutofillUnifyRationalizationAndSectioningOrder is launched.
-    RationalizeFormStructure(client_country, current_page_language,
-                             log_manager);
-    RationalizePhoneNumberFieldsForFilling();
-    AssignSections(fields_);
-  } else if (!legacy_order) {
-    AssignSections(fields_);
-    RationalizeFormStructure(client_country, current_page_language,
-                             log_manager);
-    RationalizePhoneNumberFieldsForFilling();
-  } else {
-    RationalizeFormStructure(client_country, current_page_language,
-                             log_manager);
-    AssignSections(fields_);
-    RationalizePhoneNumberFieldsForFilling();
-  }
+    LogManager* log_manager) {
+  // We call AssignSections() before *and* after rationalization because
+  // - rationalization depends on sections and
+  // - sectioning depends on field types, which rationalization may change.
+  AssignSections(fields_);
+  FormStructureRationalizer rationalizer(fields_);
+  // Rationalize the form's autocomplete attributes, repeated fields and field
+  // type predictions.
+  rationalizer.RationalizeContentEditables(log_manager);
+  rationalizer.RationalizeAutocompleteAttributes(log_manager);
+  rationalizer.RationalizeFieldTypePredictions(
+      main_frame_origin(), client_country, current_page_language, log_manager);
+  // Rationalize phone number fields so that, in every section, only the first
+  // complete phone number is filled automatically. This is useful for when a
+  // form contains a first phone number and second phone number, which usually
+  // should be distinct.
+  rationalizer.RationalizePhoneNumbersForFilling();
+  AssignSections(fields_);
 
   // Log the field type predicted by rationalization.
   // The sections are mapped to consecutive natural numbers starting at 1.
@@ -611,22 +604,6 @@
   return form_types;
 }
 
-void FormStructure::RationalizePhoneNumberFieldsForFilling() {
-  FormStructureRationalizer rationalizer(fields_);
-  rationalizer.RationalizePhoneNumbersForFilling();
-}
-
-void FormStructure::RationalizeFormStructure(
-    const GeoIpCountryCode& client_country,
-    const LanguageCode& current_page_language,
-    LogManager* log_manager) {
-  FormStructureRationalizer rationalizer(fields_);
-  rationalizer.RationalizeContentEditables(log_manager);
-  rationalizer.RationalizeAutocompleteAttributes(log_manager);
-  rationalizer.RationalizeFieldTypePredictions(
-      main_frame_origin(), client_country, current_page_language, log_manager);
-}
-
 std::ostream& operator<<(std::ostream& buffer, const FormStructure& form) {
   buffer << "\nForm signature: "
          << base::StrCat({base::NumberToString(form.form_signature().value()),
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index 7761483..bfd0ba5 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -75,8 +75,7 @@
   // TODO(crbug.com/408497919): Make the order consistent.
   void RationalizeAndAssignSections(const GeoIpCountryCode& client_country,
                                     const LanguageCode& current_page_language,
-                                    LogManager* log_manager,
-                                    bool legacy_order = false);
+                                    LogManager* log_manager);
 
   // Returns predictions that can be sent to the renderer process for debugging.
   FormDataPredictions GetFieldTypePredictions() const;
@@ -182,18 +181,6 @@
   void RetrieveFromCache(const FormStructure& cached_form,
                          RetrieveFromCacheReason reason);
 
-  // Rationalize phone number fields so that, in every section, only the first
-  // complete phone number is filled automatically. This is useful for when a
-  // form contains a first phone number and second phone number, which usually
-  // should be distinct.
-  void RationalizePhoneNumberFieldsForFilling();
-
-  // Rationalize the form's autocomplete attributes, repeated fields and field
-  // type predictions.
-  void RationalizeFormStructure(const GeoIpCountryCode& client_country,
-                                const LanguageCode& current_page_language,
-                                LogManager* log_manager);
-
   // Returns the FieldGlobalIds of the |fields_| that are eligible for manual
   // filling on form interaction.
   static std::vector<FieldGlobalId> FindFieldsEligibleForManualFilling(
diff --git a/components/autofill/core/browser/form_structure_rationalizer_unittest.cc b/components/autofill/core/browser/form_structure_rationalizer_unittest.cc
index a3afdbb4..3a63690 100644
--- a/components/autofill/core/browser/form_structure_rationalizer_unittest.cc
+++ b/components/autofill/core/browser/form_structure_rationalizer_unittest.cc
@@ -150,8 +150,7 @@
       response_string, {form_structure.get()},
       test::GetEncodedSignatures({form_structure.get()}), nullptr);
   form_structure->RationalizeAndAssignSections(GeoIpCountryCode(""),
-                                               LanguageCode(""), nullptr,
-                                               /*legacy_order=*/true);
+                                               LanguageCode(""), nullptr);
   return form_structure;
 }
 
diff --git a/components/autofill/core/browser/foundations/autofill_client.cc b/components/autofill/core/browser/foundations/autofill_client.cc
index 055cc15..48ea5e7 100644
--- a/components/autofill/core/browser/foundations/autofill_client.cc
+++ b/components/autofill/core/browser/foundations/autofill_client.cc
@@ -327,4 +327,8 @@
   return nullptr;
 }
 
+bool AutofillClient::DocumentUsedWebOTP() {
+  return false;
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/foundations/autofill_client.h b/components/autofill/core/browser/foundations/autofill_client.h
index 3f45637c..ea7bcbe 100644
--- a/components/autofill/core/browser/foundations/autofill_client.h
+++ b/components/autofill/core/browser/foundations/autofill_client.h
@@ -730,6 +730,11 @@
 
   // May return null on platforms where no SmsOtpBackend is supported.
   virtual one_time_tokens::SmsOtpBackend* GetSmsOtpBackend() const;
+
+  // Returns true if the primary main frame's document used the WebOTP API. This
+  // exists only for the main frame because only the main frame has the
+  // permission to call the WeOTP API.
+  virtual bool DocumentUsedWebOTP();
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/foundations/autofill_manager.cc b/components/autofill/core/browser/foundations/autofill_manager.cc
index 9bc0d12..90e05d2 100644
--- a/components/autofill/core/browser/foundations/autofill_manager.cc
+++ b/components/autofill/core/browser/foundations/autofill_manager.cc
@@ -935,8 +935,7 @@
 
   for (const raw_ptr<FormStructure, VectorExperimental> form : queried_forms) {
     form->RationalizeAndAssignSections(client().GetVariationConfigCountryCode(),
-                                       GetCurrentPageLanguage(), log_manager(),
-                                       /*legacy_order=*/true);
+                                       GetCurrentPageLanguage(), log_manager());
 
     autofill_metrics::LogQualityMetricsBasedOnAutocomplete(
         *form, client().GetFormInteractionsUkmLogger(),
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager.cc b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
index 69004cc..4c78a33 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
@@ -100,6 +100,7 @@
 #include "components/autofill/core/browser/integrators/identity_credential/identity_credential_delegate.h"
 #include "components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl.h"
 #include "components/autofill/core/browser/integrators/optimization_guide/autofill_optimization_guide_decider.h"
+#include "components/autofill/core/browser/integrators/password_form_classification.h"
 #include "components/autofill/core/browser/integrators/password_manager/password_manager_delegate.h"
 #include "components/autofill/core/browser/logging/log_manager.h"
 #include "components/autofill/core/browser/metrics/autofill_in_devtools_metrics.h"
@@ -385,14 +386,12 @@
 }
 
 // Returns true if autocomplete=unrecognized (address) fields should receive
-// suggestions. On desktop, suggestion can only be triggered for them through
-// manual fallbacks. On mobile, suggestions are always shown.
-bool ShouldShowSuggestionsForAutocompleteUnrecognizedFields(
-    AutofillSuggestionTriggerSource trigger_source) {
+// suggestions.
+bool ShouldShowSuggestionsForAutocompleteUnrecognizedFields() {
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
   return true;
 #else
-  return IsAutofillManuallyTriggered(trigger_source);
+  return false;
 #endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
 }
 
@@ -476,32 +475,6 @@
   }
 }
 
-bool ShouldOfferSingleFieldFill(const AutofillField* autofill_field,
-                                AutofillSuggestionTriggerSource trigger_source,
-                                SuppressReason suppress_reason) {
-  if (trigger_source ==
-      AutofillSuggestionTriggerSource::kTextareaFocusedWithoutClick) {
-    return false;
-  }
-  // Do not offer single field form fill suggestions for credit card number,
-  // cvc, and expiration date related fields. Standalone cvc fields (used to
-  // re-authenticate the use of a credit card the website has on file) will be
-  // handled separately because those have the field type
-  // CREDIT_CARD_STANDALONE_VERIFICATION_CODE.
-  FieldType type = autofill_field ? autofill_field->Type().GetCreditCardType()
-                                  : UNKNOWN_TYPE;
-  if (data_util::IsCreditCardExpirationType(type) ||
-      type == CREDIT_CARD_VERIFICATION_CODE || type == CREDIT_CARD_NUMBER) {
-    return false;
-  }
-
-  // Do not offer single field form fill suggestions if popups are suppressed
-  // due to an unrecognized autocomplete attribute. Note that in the context
-  // of Autofill, the popup for credit card related fields is not getting
-  // suppressed due to an unrecognized autocomplete attribute.
-  return suppress_reason != SuppressReason::kAutocompleteUnrecognized;
-}
-
 // Returns whether suggestions should be suppressed for the given reason.
 bool ShouldSuppressSuggestions(SuppressReason suppress_reason,
                                LogManager* log_manager) {
@@ -569,7 +542,7 @@
   context.filling_product =
       GetPreferredSuggestionFillingProduct(autofill_field->Type());
 
-  if (!ShouldShowSuggestionsForAutocompleteUnrecognizedFields(trigger_source) &&
+  if (!ShouldShowSuggestionsForAutocompleteUnrecognizedFields() &&
       autofill_field->ShouldSuppressSuggestionsAndFillingByDefault()) {
     // If non-Autocomplete suggestions may be shown on some other field of the
     // form, we want to suppress Autocomplete suggestions on this field.
@@ -810,7 +783,7 @@
 BrowserAutofillManager::BrowserAutofillManager(AutofillDriver* driver)
     : AutofillManager(driver),
       otp_manager_(
-          new OtpManagerImpl(this,
+          new OtpManagerImpl(*this,
                              driver->GetAutofillClient().GetSmsOtpBackend())),
       account_name_email_strike_manager_(
           std::make_unique<AccountNameEmailStrikeManager>(*this)) {}
@@ -1138,6 +1111,8 @@
 #endif  // !BUILDFLAG(IS_ANDROID)
       return;
     }
+  } else if (IsPasswordsAutofillManuallyTriggered(trigger_source)) {
+    return;
   }
 
   if (base::FeatureList::IsEnabled(features::kAutofillDisableFilling)) {
@@ -1507,15 +1482,16 @@
     return;
   }
 
-  // Check if other suggestion sources should be queried. Other suggestions may
-  // include Compose or single field form suggestions. Manual fallbacks can't
-  // trigger different suggestion types.
-  const bool should_offer_other_suggestions =
-      suggestions.empty() && !IsAutofillManuallyTriggered(trigger_source);
+  if (!suggestions.empty()) {
+    // Show the list of `suggestions` if not empty. These may include address or
+    // credit card suggestions. Additionally, warnings about mixed content might
+    // be present.
+    std::move(callback).Run(/*show_suggestions=*/true, std::move(suggestions));
+    return;
+  }
 
-  if (should_offer_other_suggestions &&
-      (field.form_control_type() == FormControlType::kTextArea ||
-       field.form_control_type() == FormControlType::kContentEditable)) {
+  if (field.form_control_type() == FormControlType::kTextArea ||
+      field.form_control_type() == FormControlType::kContentEditable) {
     AutofillComposeDelegate* compose_delegate = client().GetComposeDelegate();
     std::optional<Suggestion> maybe_compose_suggestion =
         compose_delegate
@@ -1527,35 +1503,18 @@
                               {*std::move(maybe_compose_suggestion)});
       return;
     }
-  }
-
-  if (!suggestions.empty()) {
-    // Show the list of `suggestions` if not empty. These may include address or
-    // credit card suggestions. Additionally, warnings about mixed content might
-    // be present.
-    std::move(callback).Run(/*show_suggestions=*/true, std::move(suggestions));
+  } else if (IsTriggerSourceOnlyRelevantForCompose(trigger_source)) {
+    std::move(callback).Run(/*show_suggestions=*/true,
+                            /*suggestions=*/{});
     return;
   }
 
-  // Whether or not to request single field form fill suggestions.
-  const bool should_offer_single_field_form_fill =
-      should_offer_other_suggestions &&
-      ShouldOfferSingleFieldFill(autofill_field, trigger_source,
-                                 context.suppress_reason);
-
-  // Whether or not to show plus address suggestions.
   const bool should_offer_plus_addresses =
       plus_addresses && autofill_field &&
       (autofill_field->Type().GetGroups().contains(FieldTypeGroup::kEmail) ||
        autofill_field->Type().GetTypes().contains(FieldType::USERNAME) ||
        autofill_field->Type().GetTypes().contains(FieldType::SINGLE_USERNAME));
 
-  // Early return to avoid running password form classifications.
-  if (!should_offer_plus_addresses && !should_offer_single_field_form_fill) {
-    std::move(callback).Run(/*show_suggestions=*/true, std::move(suggestions));
-    return;
-  }
-
   const PasswordFormClassification password_form_classification =
       client().ClassifyAsPasswordForm(*this, form.global_id(),
                                       field.global_id());
@@ -1576,48 +1535,41 @@
       weak_ptr_factory_.GetWeakPtr(),
       AutofillPlusAddressDelegate::SuggestionContext::kAutocomplete,
       password_form_classification.type, form.global_id(), field,
-      should_offer_single_field_form_fill, std::move(callback),
-      std::move(plus_address_suggestions));
+      std::move(callback), std::move(plus_address_suggestions));
 
-  if (should_offer_single_field_form_fill) {
-    // Generating single field suggestions.
-    auto on_suggestions_returned = base::BindOnce(
-        [](base::OnceCallback<void(std::vector<Suggestion>)> callback,
-           FieldGlobalId field_id, const std::vector<Suggestion>& suggestions) {
-          std::move(callback).Run(suggestions);
-        },
-        std::move(on_single_field_suggestions_callback));
-    if (form_structure && autofill_field &&
-        client().GetPaymentsAutofillClient()->GetMerchantPromoCodeManager() &&
-        client()
-            .GetPaymentsAutofillClient()
-            ->GetMerchantPromoCodeManager()
-            ->OnGetSingleFieldSuggestions(*form_structure, field,
-                                          *autofill_field, client(),
-                                          on_suggestions_returned)) {
-      return;
-    }
-    if (form_structure && autofill_field &&
-        client().GetPaymentsAutofillClient()->GetIbanManager() &&
-        client()
-            .GetPaymentsAutofillClient()
-            ->GetIbanManager()
-            ->OnGetSingleFieldSuggestions(*form_structure, field,
-                                          *autofill_field, client(),
-                                          on_suggestions_returned)) {
-      return;
-    }
-    // Autocomplete suggestions have to be generated last since they have to
-    // take the ownership of `on_suggestions_returned`.
-    // Even if no autocomplete suggestions are generated,
-    // `on_suggestions_returned` is still called with an empty list of
-    // suggestions.
-    client().GetAutocompleteHistoryManager()->OnGetSingleFieldSuggestions(
-            form, field, client(), std::move(on_suggestions_returned));
-  } else {
-    std::move(on_single_field_suggestions_callback)
-        .Run(/*single_field_suggestions=*/{});
+  // Generating single field suggestions.
+  auto on_suggestions_returned = base::BindOnce(
+      [](base::OnceCallback<void(std::vector<Suggestion>)> callback,
+         FieldGlobalId field_id, const std::vector<Suggestion>& suggestions) {
+        std::move(callback).Run(suggestions);
+      },
+      std::move(on_single_field_suggestions_callback));
+  if (form_structure && autofill_field &&
+      client().GetPaymentsAutofillClient()->GetMerchantPromoCodeManager() &&
+      client()
+          .GetPaymentsAutofillClient()
+          ->GetMerchantPromoCodeManager()
+          ->OnGetSingleFieldSuggestions(*form_structure, field, *autofill_field,
+                                        client(), on_suggestions_returned)) {
+    return;
   }
+  if (form_structure && autofill_field &&
+      client().GetPaymentsAutofillClient()->GetIbanManager() &&
+      client()
+          .GetPaymentsAutofillClient()
+          ->GetIbanManager()
+          ->OnGetSingleFieldSuggestions(*form_structure, field, *autofill_field,
+                                        client(), on_suggestions_returned)) {
+    return;
+  }
+  // Autocomplete suggestions have to be generated last since they have to
+  // take the ownership of `on_suggestions_returned`.
+  // Even if no autocomplete suggestions are generated,
+  // `on_suggestions_returned` is still called with an empty list of
+  // suggestions.
+  client().GetAutocompleteHistoryManager()->OnGetSingleFieldSuggestions(
+      form, form_structure, field, autofill_field, client(),
+      std::move(on_suggestions_returned));
 }
 
 void BrowserAutofillManager::
@@ -1626,7 +1578,6 @@
         PasswordFormClassification::Type password_form_type,
         const FormGlobalId& form_id,
         const FormFieldData& field,
-        bool should_offer_single_field_form_fill,
         OnGenerateSuggestionsCallback callback,
         std::vector<Suggestion> plus_address_suggestions,
         std::vector<Suggestion> single_field_suggestions) {
@@ -1646,7 +1597,7 @@
     // suggestions.
     // TODO(crbug.com/381994105): Consider adding
     // `should_offer_autofill_on_typing()` to `FormFieldData`.
-    if (should_offer_single_field_form_fill && field.should_autocomplete() &&
+    if (field.should_autocomplete() &&
         base::FeatureList::IsEnabled(
             features::kAutofillAddressSuggestionsOnTyping)) {
       // Try to build `Suggestion::kAddressEntryOnTyping` suggestions.
@@ -2651,7 +2602,7 @@
   credit_card_access_manager_.reset();
   // Forget cached OTPs after a navigation.
   otp_manager_ = std::make_unique<OtpManagerImpl>(
-      this, driver().GetAutofillClient().GetSmsOtpBackend());
+      *this, driver().GetAutofillClient().GetSmsOtpBackend());
   account_name_email_strike_manager_ =
       std::make_unique<AccountNameEmailStrikeManager>(*this);
   metrics_.reset();
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager.h b/components/autofill/core/browser/foundations/browser_autofill_manager.h
index f2be2b0..b6deb8fa2 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager.h
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager.h
@@ -567,7 +567,6 @@
       PasswordFormClassification::Type password_form_type,
       const FormGlobalId& form_id,
       const FormFieldData& field,
-      bool should_offer_single_field_form_fill,
       OnGenerateSuggestionsCallback callback,
       std::vector<Suggestion> plus_address_suggestions,
       std::vector<Suggestion> single_field_suggestions);
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
index d6f53d3..2a92fff 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
@@ -808,29 +808,6 @@
         .set_credit_card_save_manager(create_credit_card_save_manager());
     test_api(*GetFormDataImporter())
         .set_iban_save_manager(std::make_unique<IbanSaveManager>(this));
-
-    // By default, if we offer single field form fill, suggestions should be
-    // returned because it is assumed `field.should_autocomplete` is set to
-    // true. This should be overridden in tests where
-    // `field.should_autocomplete` is set to false or other conditions for
-    // different providers are not met.
-    ON_CALL(*GetPaymentsAutofillClient()->GetMerchantPromoCodeManager(),
-            OnGetSingleFieldSuggestions)
-        .WillByDefault(Return(true));
-    ON_CALL(*GetPaymentsAutofillClient()->GetIbanManager(),
-            OnGetSingleFieldSuggestions)
-        .WillByDefault(Return(true));
-    MockAutocompleteHistoryManager* autocomplete_history_manager =
-        static_cast<MockAutocompleteHistoryManager*>(
-            GetAutocompleteHistoryManager());
-    ON_CALL(*autocomplete_history_manager,
-            OnGetSingleFieldSuggestions)
-        .WillByDefault([](const FormData& form, const FormFieldData& field,
-                          const AutofillClient& client,
-                          SingleFieldFillRouter::OnSuggestionsReturnedCallback
-                              on_suggestions_returned) {
-          std::move(on_suggestions_returned).Run(field.global_id(), {});
-        });
   }
 
   MOCK_METHOD(void, GetAiPageContent, (GetAiPageContentCallback), (override));
@@ -3076,6 +3053,22 @@
           "", Suggestion::Icon::kNoIcon,
           SuggestionType::kInsecureContextPaymentDisabledMessage)});
 
+  // Ensure that the single field suggestions are not considered for any
+  // field.
+  EXPECT_CALL(merchant_promo_code_manager(), OnGetSingleFieldSuggestions)
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(iban_manager(), OnGetSingleFieldSuggestions)
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(autocomplete_history_manager(), OnGetSingleFieldSuggestions)
+      .WillRepeatedly(
+          [](const FormData& form, const FormStructure* form_structure,
+             const FormFieldData& field, const AutofillField* autofill_field,
+             const AutofillClient& client,
+             SingleFieldFillRouter::OnSuggestionsReturnedCallback
+                 on_suggestions_returned) {
+            std::move(on_suggestions_returned).Run(field.global_id(), {});
+          });
+
   // Clear the test credit cards and try again -- we shouldn't return a warning.
   personal_data().test_payments_data_manager().ClearCreditCards();
   OnAskForValuesToFill(form, cc_number_field);
@@ -3176,6 +3169,22 @@
   test_api(form).field(1).set_value(u"4444 4444 4444 4444");
   test_api(form).field(1).set_is_autofilled(false);
 
+  // Ensure that the single field suggestions are not considered for any
+  // field.
+  EXPECT_CALL(merchant_promo_code_manager(), OnGetSingleFieldSuggestions)
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(iban_manager(), OnGetSingleFieldSuggestions)
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(autocomplete_history_manager(), OnGetSingleFieldSuggestions)
+      .WillRepeatedly(
+          [](const FormData& form, const FormStructure* form_structure,
+             const FormFieldData& field, const AutofillField* autofill_field,
+             const AutofillClient& client,
+             SingleFieldFillRouter::OnSuggestionsReturnedCallback
+                 on_suggestions_returned) {
+            std::move(on_suggestions_returned).Run(field.global_id(), {});
+          });
+
   // Expect no suggestions are returned for the expiry type field.
   const FormFieldData& expiry_type_field = form.fields()[2];
   OnAskForValuesToFill(form, expiry_type_field);
@@ -5132,29 +5141,6 @@
   OnAskForValuesToFill(form, field);
 }
 
-// Test that when Autofill is disabled and the field is a credit card number
-// field, single field form fill suggestions are not queried.
-TEST_F(BrowserAutofillManagerTest_AutofillDisabled,
-       SingleFieldFillSuggestions_CreditCardNumberShouldNotAutocomplete) {
-  // Set up our form data.
-  FormData form = CreateTestCreditCardFormData(/*is_https=*/false,
-                                               /*use_month_type=*/false);
-  FormsSeen({form});
-  // The second field is "Card Number", which should not autocomplete.
-  FormFieldData& field = test_api(form).field(1);
-  field.set_should_autocomplete(true);
-
-  // Ensure that the single field suggestions are not considered for any
-  // field.
-  EXPECT_CALL(merchant_promo_code_manager(), OnGetSingleFieldSuggestions)
-      .Times(0);
-  EXPECT_CALL(iban_manager(), OnGetSingleFieldSuggestions).Times(0);
-  EXPECT_CALL(autocomplete_history_manager(), OnGetSingleFieldSuggestions)
-      .Times(0);
-
-  OnAskForValuesToFill(form, field);
-}
-
 // Test that the situation where there are no Autofill suggestions available,
 // and no single field form fill conditions were met is correctly handled. The
 // single field form fill conditions were not met because autocomplete is set to
@@ -5176,12 +5162,14 @@
   EXPECT_CALL(iban_manager(), OnGetSingleFieldSuggestions)
       .WillRepeatedly(Return(false));
   EXPECT_CALL(autocomplete_history_manager(), OnGetSingleFieldSuggestions)
-      .WillRepeatedly([](const FormData& form, const FormFieldData& field,
-                         const AutofillClient& client,
-                         SingleFieldFillRouter::OnSuggestionsReturnedCallback
-                             on_suggestions_returned) {
-        std::move(on_suggestions_returned).Run(field.global_id(), {});
-      });
+      .WillRepeatedly(
+          [](const FormData& form, const FormStructure* form_structure,
+             const FormFieldData& field, const AutofillField* autofill_field,
+             const AutofillClient& client,
+             SingleFieldFillRouter::OnSuggestionsReturnedCallback
+                 on_suggestions_returned) {
+            std::move(on_suggestions_returned).Run(field.global_id(), {});
+          });
 
   OnAskForValuesToFill(form, email_field);
 
@@ -5210,12 +5198,14 @@
   EXPECT_CALL(iban_manager(), OnGetSingleFieldSuggestions)
       .WillRepeatedly(Return(false));
   EXPECT_CALL(autocomplete_history_manager(), OnGetSingleFieldSuggestions)
-      .WillRepeatedly([](const FormData& form, const FormFieldData& field,
-                         const AutofillClient& client,
-                         SingleFieldFillRouter::OnSuggestionsReturnedCallback
-                             on_suggestions_returned) {
-        std::move(on_suggestions_returned).Run(field.global_id(), {});
-      });
+      .WillRepeatedly(
+          [](const FormData& form, const FormStructure* form_structure,
+             const FormFieldData& field, const AutofillField* autofill_field,
+             const AutofillClient& client,
+             SingleFieldFillRouter::OnSuggestionsReturnedCallback
+                 on_suggestions_returned) {
+            std::move(on_suggestions_returned).Run(field.global_id(), {});
+          });
 
   OnAskForValuesToFill(form, field);
 
@@ -6171,12 +6161,14 @@
     // to the field not having a type that would route to any of the other
     // single field form fillers.
     ON_CALL(autocomplete_history_manager(), OnGetSingleFieldSuggestions)
-        .WillByDefault([](const FormData& form, const FormFieldData& field,
-                          const AutofillClient& client,
-                          SingleFieldFillRouter::OnSuggestionsReturnedCallback
-                              on_suggestions_returned) {
-          std::move(on_suggestions_returned).Run(field.global_id(), {});
-        });
+        .WillByDefault(
+            [](const FormData& form, const FormStructure* form_structure,
+               const FormFieldData& field, const AutofillField* autofill_field,
+               const AutofillClient& client,
+               SingleFieldFillRouter::OnSuggestionsReturnedCallback
+                   on_suggestions_returned) {
+              std::move(on_suggestions_returned).Run(field.global_id(), {});
+            });
     OnAskForValuesToFill(mixed_form, mixed_form_field);
 
     EXPECT_TRUE(external_delegate()->on_suggestions_returned_seen());
@@ -7592,12 +7584,6 @@
 class BrowserAutofillManagerTest_AutofillAi
     : public BrowserAutofillManagerTest {
  public:
-  BrowserAutofillManagerTest_AutofillAi() {
-    feature_list_.InitWithFeatures({features::kAutofillAiWithDataSchema,
-                                    features::kAutofillUnionTypesForAutofillAi},
-                                   {});
-  }
-
   void SetUp() override {
     BrowserAutofillManagerTest::SetUp();
     autofill_client().set_entity_data_manager(
@@ -7690,7 +7676,8 @@
   }
 
  private:
-  base::test::ScopedFeatureList feature_list_;
+  base::test::ScopedFeatureList feature_list_{
+      features::kAutofillAiWithDataSchema};
   AutofillWebDataServiceTestHelper webdata_helper_{
       std::make_unique<EntityTable>()};
   FormData passport_form_;
@@ -8692,12 +8679,14 @@
   EXPECT_CALL(iban_manager(), OnGetSingleFieldSuggestions)
       .WillRepeatedly(Return(false));
   EXPECT_CALL(autocomplete_history_manager(), OnGetSingleFieldSuggestions)
-      .WillRepeatedly([](const FormData& form, const FormFieldData& field,
-                         const AutofillClient& client,
-                         SingleFieldFillRouter::OnSuggestionsReturnedCallback
-                             on_suggestions_returned) {
-        std::move(on_suggestions_returned).Run(field.global_id(), {});
-      });
+      .WillRepeatedly(
+          [](const FormData& form, const FormStructure* form_structure,
+             const FormFieldData& field, const AutofillField* autofill_field,
+             const AutofillClient& client,
+             SingleFieldFillRouter::OnSuggestionsReturnedCallback
+                 on_suggestions_returned) {
+            std::move(on_suggestions_returned).Run(field.global_id(), {});
+          });
   FormsSeen({form});
   OnAskForValuesToFill(form, form.fields()[0]);
 
@@ -9002,12 +8991,14 @@
   EXPECT_CALL(iban_manager(), OnGetSingleFieldSuggestions)
       .WillRepeatedly(Return(false));
   EXPECT_CALL(autocomplete_history_manager(), OnGetSingleFieldSuggestions)
-      .WillRepeatedly([](const FormData& form, const FormFieldData& field,
-                         const AutofillClient& client,
-                         SingleFieldFillRouter::OnSuggestionsReturnedCallback
-                             on_suggestions_returned) {
-        std::move(on_suggestions_returned).Run(field.global_id(), {});
-      });
+      .WillRepeatedly(
+          [](const FormData& form, const FormStructure* form_structure,
+             const FormFieldData& field, const AutofillField* autofill_field,
+             const AutofillClient& client,
+             SingleFieldFillRouter::OnSuggestionsReturnedCallback
+                 on_suggestions_returned) {
+            std::move(on_suggestions_returned).Run(field.global_id(), {});
+          });
 
   EXPECT_CALL(plus_address_delegate(),
               OnPlusAddressSuggestionShown(
@@ -9150,7 +9141,10 @@
   EXPECT_CALL(iban_manager(), OnGetSingleFieldSuggestions)
       .WillRepeatedly(Return(false));
   EXPECT_CALL(autocomplete_history_manager(), OnGetSingleFieldSuggestions)
-      .WillRepeatedly([&](const FormData& form, const FormFieldData& field,
+      .WillRepeatedly([&](const FormData& form,
+                          const FormStructure* form_structure,
+                          const FormFieldData& field,
+                          const AutofillField* autofill_field,
                           const AutofillClient&,
                           SingleFieldFillRouter::OnSuggestionsReturnedCallback
                               on_suggestions_returned) {
@@ -9208,12 +9202,14 @@
   EXPECT_CALL(iban_manager(), OnGetSingleFieldSuggestions)
       .WillRepeatedly(Return(false));
   EXPECT_CALL(autocomplete_history_manager(), OnGetSingleFieldSuggestions)
-      .WillRepeatedly([](const FormData& form, const FormFieldData& field,
-                         const AutofillClient& client,
-                         SingleFieldFillRouter::OnSuggestionsReturnedCallback
-                             on_suggestions_returned) {
-        std::move(on_suggestions_returned).Run(field.global_id(), {});
-      });
+      .WillRepeatedly(
+          [](const FormData& form, const FormStructure* form_structure,
+             const FormFieldData& field, const AutofillField* autofill_field,
+             const AutofillClient& client,
+             SingleFieldFillRouter::OnSuggestionsReturnedCallback
+                 on_suggestions_returned) {
+            std::move(on_suggestions_returned).Run(field.global_id(), {});
+          });
 
   // Set up our form data. Notably, the first field is an email address.
   FormData form = test::GetFormData(
diff --git a/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl.cc b/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl.cc
index b149636..67af66b 100644
--- a/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl.cc
+++ b/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl.cc
@@ -31,12 +31,10 @@
 
 }  // namespace
 
-OtpManagerImpl::OtpManagerImpl(BrowserAutofillManager* owner,
+OtpManagerImpl::OtpManagerImpl(BrowserAutofillManager& owner,
                                one_time_tokens::SmsOtpBackend* sms_otp_backend)
     : owner_(owner), sms_otp_backend_(sms_otp_backend) {
-  if (owner_) {
-    autofill_manager_observation_.Observe(owner);
-  }
+  autofill_manager_observation_.Observe(&owner);
 
   // TODO(crbug.com/415273270) This is just a hack to prepopulate the OTPs in
   // case no real backend is triggered. The feature definition should migrate to
@@ -56,11 +54,25 @@
 
 void OtpManagerImpl::GetOtpSuggestions(
     OtpManagerImpl::GetOtpSuggestionsCallback callback) {
+  // If a website uses the WebOTP API, GMSCore or Chrome will show its own UI to
+  // fill the OTP. `wrapped_callback` prevents that an SMS OTP is delivered in
+  // case the WebOTP API was used.
+  GetOtpSuggestionsCallback wrapped_callback = base::BindOnce(
+      [](base::WeakPtr<OtpManagerImpl> self,
+         OtpManagerImpl::GetOtpSuggestionsCallback callback,
+         std::vector<std::string> suggestions) {
+        if (!self || self->IsOtpDeliveryBlocked()) {
+          suggestions.clear();
+        }
+        std::move(callback).Run(std::move(suggestions));
+      },
+      weak_ptr_factory_.GetWeakPtr(), std::move(callback));
+
   if (!sms_otp_retrieval_in_progress_) {
     FilterExpiredOtps(otp_suggestions_);
-    std::move(callback).Run(OtpsToSuggestionStrings(otp_suggestions_));
+    std::move(wrapped_callback).Run(OtpsToSuggestionStrings(otp_suggestions_));
   } else {
-    last_pending_get_suggestions_callback_ = std::move(callback);
+    last_pending_get_suggestions_callback_ = std::move(wrapped_callback);
   }
 }
 
@@ -117,7 +129,7 @@
                   });
     otp_suggestions_.push_back(reply.otp_value.value());
 
-    if (owner_ && owner_->GetMetricState().has_value()) {
+    if (owner_->GetMetricState().has_value()) {
       owner_->GetMetricState()->otp_form_event_logger.OnOtpAvailable();
     }
   }
@@ -132,4 +144,8 @@
   // succeeds or fails, in combination with the OTP source.
 }
 
+bool OtpManagerImpl::IsOtpDeliveryBlocked() {
+  return owner_->client().DocumentUsedWebOTP();
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl.h b/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl.h
index 18885df..588555b7 100644
--- a/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl.h
+++ b/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl.h
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/functional/callback.h"
+#include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "components/autofill/core/browser/foundations/autofill_manager.h"
@@ -34,7 +35,7 @@
   using GetOtpSuggestionsCallback =
       base::OnceCallback<void(std::vector<std::string>)>;
 
-  OtpManagerImpl(BrowserAutofillManager* owner,
+  OtpManagerImpl(BrowserAutofillManager& owner,
                  one_time_tokens::SmsOtpBackend* sms_otp_backend);
   OtpManagerImpl(const OtpManagerImpl&) = delete;
   OtpManagerImpl& operator=(const OtpManagerImpl&) = delete;
@@ -56,8 +57,12 @@
   // Handler for when the SMS backend returns OTPs.
   void OnOtpRetrievalComplete(const one_time_tokens::OtpFetchReply& reply);
 
+  // Returns true if an OTP must not be delivered to the caller in an autofill
+  // context, e.g., because the page called the WebOTP API.
+  bool IsOtpDeliveryBlocked();
+
   // The owning BrowserAutofillManager.
-  raw_ptr<BrowserAutofillManager> owner_;
+  raw_ref<BrowserAutofillManager> owner_;
 
   // May be nullptr on platforms that don't support SMS OTP fetching.
   raw_ptr<one_time_tokens::SmsOtpBackend> sms_otp_backend_ = nullptr;
diff --git a/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl_unittest.cc b/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl_unittest.cc
index 19ab66c8..1c3ddf19 100644
--- a/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl_unittest.cc
+++ b/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl_unittest.cc
@@ -100,26 +100,10 @@
   MockSmsOtpBackend sms_otp_backend_;
 };
 
-// Tests that `GetOtpSuggestions` returns no results if the OtpManagerImpl has
-// no `SmsOtpBackend`.
-TEST_F(OtpManagerImplTest, GetOtpSuggestions_NoBackend) {
-  OtpManagerImpl otp_manager(&autofill_manager(), nullptr);
-
-  // Notifying the OTP manager about the OTP field would trigger an SMS fetch
-  // if there was an SMS backend registered.
-  AddFormWithOtpField();
-
-  // As no SMS backend is registered, we get the an empty response.
-  base::test::TestFuture<const std::vector<std::string>> future;
-  otp_manager.GetOtpSuggestions(future.GetCallback());
-  EXPECT_TRUE(future.IsReady());
-  EXPECT_TRUE(future.Get().empty());
-}
-
 // Tests that no query is issued to the SMS backend if a form does not contain
 // an OTP field.
 TEST_F(OtpManagerImplTest, GetOtpSuggestions_NoQueryIssued) {
-  OtpManagerImpl otp_manager(&autofill_manager(), &sms_otp_backend_);
+  OtpManagerImpl otp_manager(autofill_manager(), &sms_otp_backend_);
 
   // As the form has no OTP field, SMS backend is not queried.
   EXPECT_CALL(sms_otp_backend_, RetrieveSmsOtp).Times(0);
@@ -136,7 +120,7 @@
 // `SmsOtpBackend` the first time it is called, and that the results are
 // correctly passed to the callback.
 TEST_F(OtpManagerImplTest, GetOtpSuggestions_TriggersFirstRetrieval) {
-  OtpManagerImpl otp_manager(&autofill_manager(), &sms_otp_backend_);
+  OtpManagerImpl otp_manager(autofill_manager(), &sms_otp_backend_);
 
   // Prepare the handling of SMS requests from the SMS backend.
   one_time_tokens::OtpFetchReply reply = GetDefaultOtpFetchReply();
@@ -156,7 +140,7 @@
 // Tests that `GetOtpSuggestions` waits with the callback if an SMS OTP
 // retrieval is in progress.
 TEST_F(OtpManagerImplTest, GetOtpSuggestions_DoesNotTriggerWhileInProgress) {
-  OtpManagerImpl otp_manager(&autofill_manager(), &sms_otp_backend_);
+  OtpManagerImpl otp_manager(autofill_manager(), &sms_otp_backend_);
 
   // Prepare the handling of SMS requests from the SMS backend.
   one_time_tokens::OtpFetchReply reply = GetDefaultOtpFetchReply();
@@ -188,7 +172,7 @@
 // Tests that `GetOtpSuggestions` immediately returns any OTPs that have
 // already been fetched.
 TEST_F(OtpManagerImplTest, GetOtpSuggestions_FetchesSmsOnlyOnce) {
-  OtpManagerImpl otp_manager(&autofill_manager(), &sms_otp_backend_);
+  OtpManagerImpl otp_manager(autofill_manager(), &sms_otp_backend_);
 
   // Prepare the handling of SMS requests from the SMS backend.
   one_time_tokens::OtpFetchReply reply = GetDefaultOtpFetchReply();
@@ -219,7 +203,7 @@
 // Tests that if `GetOtpSuggestions` is called twice, only the callback from
 // the second call is run when OTPs are fetched.
 TEST_F(OtpManagerImplTest, GetOtpSuggestions_NewCallInvalidatesOldCallback) {
-  OtpManagerImpl otp_manager(&autofill_manager(), &sms_otp_backend_);
+  OtpManagerImpl otp_manager(autofill_manager(), &sms_otp_backend_);
 
   // Prepare the handling of SMS requests from the SMS backend.
   one_time_tokens::OtpFetchReply reply = GetDefaultOtpFetchReply();
@@ -261,7 +245,7 @@
 
 // Tests that an empty OTP value received from the backend is not stored.
 TEST_F(OtpManagerImplTest, GetOtpSuggestions_EmptyOtpIsNotStored) {
-  OtpManagerImpl otp_manager(&autofill_manager(), &sms_otp_backend_);
+  OtpManagerImpl otp_manager(autofill_manager(), &sms_otp_backend_);
 
   // Prepare a reply with an empty OTP.
   one_time_tokens::OtpFetchReply reply = one_time_tokens::OtpFetchReply(
@@ -283,7 +267,7 @@
 
 // Tests that `GetOtpSuggestions` filters out expired OTPs.
 TEST_F(OtpManagerImplTest, GetOtpSuggestions_FiltersExpiredOtps) {
-  OtpManagerImpl otp_manager(&autofill_manager(), &sms_otp_backend_);
+  OtpManagerImpl otp_manager(autofill_manager(), &sms_otp_backend_);
 
   // Prepare the reply from the SMS backend.
   one_time_tokens::OtpFetchReply reply = one_time_tokens::OtpFetchReply(
diff --git a/components/autofill/core/browser/metrics/quality_metrics_unittest.cc b/components/autofill/core/browser/metrics/quality_metrics_unittest.cc
index 79ee4daa0..97e3474 100644
--- a/components/autofill/core/browser/metrics/quality_metrics_unittest.cc
+++ b/components/autofill/core/browser/metrics/quality_metrics_unittest.cc
@@ -295,7 +295,8 @@
   FormStructure* form_structure =
       autofill_manager().FindCachedFormById(form.global_id());
   ASSERT_TRUE(form_structure);
-  form_structure->RationalizePhoneNumberFieldsForFilling();
+  form_structure->RationalizeAndAssignSections(GeoIpCountryCode(""),
+                                               LanguageCode(""), nullptr);
 
   base::HistogramTester histogram_tester;
   SubmitForm(form);
@@ -333,7 +334,8 @@
   FormStructure* form_structure =
       autofill_manager().FindCachedFormById(form.global_id());
   ASSERT_TRUE(form_structure);
-  form_structure->RationalizePhoneNumberFieldsForFilling();
+  form_structure->RationalizeAndAssignSections(GeoIpCountryCode(""),
+                                               LanguageCode(""), nullptr);
 
   base::HistogramTester histogram_tester;
   SubmitForm(form);
@@ -376,7 +378,8 @@
   FormStructure* form_structure =
       autofill_manager().FindCachedFormById(form.global_id());
   ASSERT_TRUE(form_structure);
-  form_structure->RationalizePhoneNumberFieldsForFilling();
+  form_structure->RationalizeAndAssignSections(GeoIpCountryCode(""),
+                                               LanguageCode(""), nullptr);
 
   base::HistogramTester histogram_tester;
   SubmitForm(form);
@@ -427,7 +430,8 @@
   FormStructure* form_structure =
       autofill_manager().FindCachedFormById(form.global_id());
   ASSERT_TRUE(form_structure);
-  form_structure->RationalizePhoneNumberFieldsForFilling();
+  form_structure->RationalizeAndAssignSections(GeoIpCountryCode(""),
+                                               LanguageCode(""), nullptr);
 
   base::HistogramTester histogram_tester;
   SubmitForm(form);
diff --git a/components/autofill/core/browser/ml_model/field_classification_model_executor_perftest.cc b/components/autofill/core/browser/ml_model/field_classification_model_executor_perftest.cc
index a0abb7b..2451a2d3 100644
--- a/components/autofill/core/browser/ml_model/field_classification_model_executor_perftest.cc
+++ b/components/autofill/core/browser/ml_model/field_classification_model_executor_perftest.cc
@@ -65,7 +65,9 @@
       /*model_inference_timeout=*/std::nullopt,
       optimization_guide::proto::
           OPTIMIZATION_TARGET_AUTOFILL_FIELD_CLASSIFICATION,
-      runner, base::SequencedTaskRunner::GetCurrentDefault());
+      /*model_loading_task_runner=*/runner,
+      /*execution_task_runner=*/runner,
+      base::SequencedTaskRunner::GetCurrentDefault());
   runner->PostTask(
     FROM_HERE,
     base::BindOnce(&ModelExecutor::UpdateModelFile,
diff --git a/components/autofill/core/browser/ml_model/field_classification_model_executor_unittest.cc b/components/autofill/core/browser/ml_model/field_classification_model_executor_unittest.cc
index 2ce5622..9024211 100644
--- a/components/autofill/core/browser/ml_model/field_classification_model_executor_unittest.cc
+++ b/components/autofill/core/browser/ml_model/field_classification_model_executor_unittest.cc
@@ -38,32 +38,34 @@
                            .AppendASCII("autofill")
                            .AppendASCII("ml_model")
                            .AppendASCII("autofill_model-fold-one.tflite");
-    execution_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
+    task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
         {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
     model_executor_ = std::make_unique<FieldClassificationModelExecutor>();
     model_executor_->InitializeAndMoveToExecutionThread(
         /*model_inference_timeout=*/std::nullopt,
         optimization_guide::proto::
             OPTIMIZATION_TARGET_AUTOFILL_FIELD_CLASSIFICATION,
-        execution_task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
+        /*model_loading_task_runner=*/task_runner_,
+        /*execution_task_runner=*/task_runner_,
+        base::SequencedTaskRunner::GetCurrentDefault());
   }
 
   void TearDown() override {
-    execution_task_runner_->DeleteSoon(FROM_HERE, std::move(model_executor_));
+    task_runner_->DeleteSoon(FROM_HERE, std::move(model_executor_));
     task_environment_.RunUntilIdle();
   }
 
  protected:
   test::AutofillUnitTestEnvironment autofill_environment_;
   base::test::TaskEnvironment task_environment_;
-  scoped_refptr<base::SequencedTaskRunner> execution_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
   base::FilePath model_file_path_;
   std::unique_ptr<FieldClassificationModelExecutor> model_executor_;
 };
 
 TEST_F(FieldClassificationModelExecutorTest, ExecuteModel) {
   // Update model file.
-  execution_task_runner_->PostTask(
+  task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&ModelExecutor::UpdateModelFile,
                                 model_executor_->GetWeakPtrForExecutionThread(),
                                 model_file_path_));
@@ -93,7 +95,7 @@
   base::test::TestFuture<
       const std::optional<FieldClassificationModelEncoder::ModelOutput>&>
       predictions;
-  execution_task_runner_->PostTask(
+  task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&ModelExecutor::SendForExecution,
                                 model_executor_->GetWeakPtrForExecutionThread(),
                                 predictions.GetCallback(),
diff --git a/components/autofill/core/browser/ml_model/field_classification_model_handler.cc b/components/autofill/core/browser/ml_model/field_classification_model_handler.cc
index 925c738..760aeb8 100644
--- a/components/autofill/core/browser/ml_model/field_classification_model_handler.cc
+++ b/components/autofill/core/browser/ml_model/field_classification_model_handler.cc
@@ -348,11 +348,33 @@
     std::vector<FormData> forms,
     const GeoIpCountryCode& client_country,
     base::OnceCallback<void(std::vector<ModelPredictions>)> callback) {
-  auto barrier_callback = base::BarrierCallback<ModelPredictions>(
-      forms.size(), std::move(callback));
-  for (FormData& form : std::move(forms)) {
-    GetModelPredictionsForForm(std::move(form), client_country,
-                               barrier_callback);
+  // `base::BarrierCallback` is not guaranteed to gather the results in order so
+  // we have to sort them.
+  using IndexAndOutput = std::pair<size_t, ModelPredictions>;
+  auto sort_results =
+      base::BindOnce([](std::vector<IndexAndOutput> indexed_predictions)
+                         -> std::vector<ModelPredictions> {
+        std::ranges::sort(indexed_predictions,
+                          [](IndexAndOutput& a, IndexAndOutput& b) {
+                            return a.first < b.first;
+                          });
+        return base::ToVector(std::move(indexed_predictions),
+                              [](IndexAndOutput& p) -> ModelPredictions&& {
+                                return std::move(p.second);
+                              });
+      });
+  auto barrier_callback = base::BarrierCallback<IndexAndOutput>(
+      forms.size(), std::move(sort_results).Then(std::move(callback)));
+  for (size_t form_index = 0; form_index < forms.size(); ++form_index) {
+    GetModelPredictionsForForm(
+        std::move(forms[form_index]), client_country,
+        base::BindOnce(
+            [](size_t form_index,
+               ModelPredictions prediction) -> IndexAndOutput {
+              return {form_index, std::move(prediction)};
+            },
+            form_index)
+            .Then(barrier_callback));
   }
 }
 
diff --git a/components/autofill/core/browser/payments/amount_extraction_manager.cc b/components/autofill/core/browser/payments/amount_extraction_manager.cc
index c31452d..ef51e749 100644
--- a/components/autofill/core/browser/payments/amount_extraction_manager.cc
+++ b/components/autofill/core/browser/payments/amount_extraction_manager.cc
@@ -22,6 +22,8 @@
 #include "components/autofill/core/browser/payments/bnpl_manager.h"
 #include "components/autofill/core/browser/payments/constants.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
+#include "components/optimization_guide/core/optimization_guide_model_executor.h"
+#include "components/optimization_guide/proto/features/amount_extraction.pb.h"
 #include "components/optimization_guide/proto/features/common_quality_data.pb.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "url/gurl.h"
@@ -137,6 +139,26 @@
   // TODO(crbug.com/444683986): Log ApcGenerationResult to UMA.
 }
 
+void AmountExtractionManager::TriggerCheckoutAmountExtractionWithAi() {
+  if (!ai_page_content_) {
+    // TODO(crbug.com/444685164) If the member variable `ai_page_content_` is
+    // not initialized, another attempt to fetch it will be made. Retry only
+    // once.
+    return;
+  }
+
+  // Construct request
+  optimization_guide::proto::AmountExtractionRequest request;
+  *request.mutable_annotated_page_content() = std::move(*ai_page_content_);
+  ai_page_content_.reset();
+
+  autofill_manager_->client().GetOptimizationGuideModelExecutor()->ExecuteModel(
+      optimization_guide::ModelBasedCapabilityKey::kAmountExtraction,
+      std::move(request), kAiBasedAmountExtractionWaitTime,
+      base::BindOnce(&AmountExtractionManager::OnCheckoutAmountReceivedFromAi,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
 void AmountExtractionManager::TriggerCheckoutAmountExtraction() {
   if (search_request_pending_) {
     return;
@@ -197,6 +219,13 @@
   }
 }
 
+void AmountExtractionManager::OnCheckoutAmountReceivedFromAi(
+    optimization_guide::OptimizationGuideModelExecutionResult result,
+    std::unique_ptr<optimization_guide::ModelQualityLogEntry> log_entry) {
+  // TODO(crbug.com/444685164) Add logic to handle BNPL flow once the model
+  // executor response comes back.
+}
+
 void AmountExtractionManager::OnTimeoutReached() {
   // If the amount is found, ignore this callback.
   if (!search_request_pending_) {
diff --git a/components/autofill/core/browser/payments/amount_extraction_manager.h b/components/autofill/core/browser/payments/amount_extraction_manager.h
index 9ca9c0b..869e8cb 100644
--- a/components/autofill/core/browser/payments/amount_extraction_manager.h
+++ b/components/autofill/core/browser/payments/amount_extraction_manager.h
@@ -20,9 +20,15 @@
 class BrowserAutofillManager;
 }  // namespace autofill
 
-namespace optimization_guide::proto {
+namespace optimization_guide {
+
+namespace proto {
 class AnnotatedPageContent;
-}
+}  // namespace proto
+
+class ModelQualityLogEntry;
+struct OptimizationGuideModelExecutionResult;
+}  // namespace optimization_guide
 
 namespace autofill::payments {
 
@@ -55,10 +61,14 @@
       delete;
   virtual ~AmountExtractionManager();
 
-  // Timeout limit for the amount extraction in millisecond.
+  // Timeout limit for the regex-base amount extraction in millisecond.
   static constexpr base::TimeDelta kAmountExtractionWaitTime =
       base::Milliseconds(150);
 
+  // Timeout limit for the ai-based amount extraction in millisecond.
+  static constexpr base::TimeDelta kAiBasedAmountExtractionWaitTime =
+      base::Seconds(10);
+
   // This function attempts to convert a string representation of a monetary
   // value in dollars into a uint64_t by parsing it as a double and multiplying
   // the result by 1,000,000. It assumes the input uses a decimal point ('.') as
@@ -97,6 +107,9 @@
   // current page.
   virtual void TriggerCheckoutAmountExtraction();
 
+  // Trigger the search for the final checkout amount using server-side AI.
+  virtual void TriggerCheckoutAmountExtractionWithAi();
+
  private:
   friend class AmountExtractionManagerTest;
   friend class AmountExtractionManagerTestApi;
@@ -109,6 +122,11 @@
       base::TimeTicks search_request_start_timestamp,
       const std::string& extracted_amount);
 
+  // Invoked once the amount extraction from the model executor is complete.
+  virtual void OnCheckoutAmountReceivedFromAi(
+      optimization_guide::OptimizationGuideModelExecutionResult result,
+      std::unique_ptr<optimization_guide::ModelQualityLogEntry> log_entry);
+
   // Checks whether the current amount search has reached the timeout or not.
   // If so, cancel the ongoing search.
   virtual void OnTimeoutReached();
diff --git a/components/autofill/core/browser/payments/amount_extraction_manager_test_api.h b/components/autofill/core/browser/payments/amount_extraction_manager_test_api.h
index e960c18..3479c383 100644
--- a/components/autofill/core/browser/payments/amount_extraction_manager_test_api.h
+++ b/components/autofill/core/browser/payments/amount_extraction_manager_test_api.h
@@ -5,8 +5,11 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_AMOUNT_EXTRACTION_MANAGER_TEST_API_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_AMOUNT_EXTRACTION_MANAGER_TEST_API_H_
 
+#include <memory>
+
 #include "base/check_deref.h"
 #include "components/autofill/core/browser/payments/amount_extraction_manager.h"
+#include "components/optimization_guide/proto/features/common_quality_data.pb.h"
 
 namespace autofill::payments {
 
@@ -43,6 +46,12 @@
     return amount_extraction_manager_->ai_page_content_.get();
   }
 
+  void SetAiPageContent() {
+    amount_extraction_manager_->ai_page_content_ =
+        std::make_unique<optimization_guide::proto::AnnotatedPageContent>(
+            optimization_guide::proto::AnnotatedPageContent());
+  }
+
  private:
   const raw_ref<AmountExtractionManager> amount_extraction_manager_;
 };
diff --git a/components/autofill/core/browser/payments/amount_extraction_manager_unittest.cc b/components/autofill/core/browser/payments/amount_extraction_manager_unittest.cc
index 84d06ab5..48c6a780 100644
--- a/components/autofill/core/browser/payments/amount_extraction_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/amount_extraction_manager_unittest.cc
@@ -4,8 +4,13 @@
 
 #include "components/autofill/core/browser/payments/amount_extraction_manager.h"
 
+#include <memory>
+
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/mock_callback.h"
+#include "base/test/protobuf_matchers.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "components/autofill/core/browser/data_manager/payments/payments_data_manager_test_api.h"
@@ -21,6 +26,8 @@
 #include "components/autofill/core/browser/payments/test/mock_bnpl_manager.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
+#include "components/optimization_guide/core/mock_optimization_guide_model_executor.h"
+#include "components/optimization_guide/core/optimization_guide_proto_util.h"
 #include "components/optimization_guide/proto/features/common_quality_data.pb.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -29,13 +36,18 @@
 namespace autofill::payments {
 
 namespace {
+using base::test::EqualsProto;
 using ::testing::_;
+using ::testing::A;
 using ::testing::ElementsAre;
 using ::testing::Eq;
 using ::testing::IsEmpty;
 using ::testing::NiceMock;
 using ::testing::Return;
 using ::testing::Test;
+using ModelExecutionCallback = base::OnceCallback<void(
+    optimization_guide::OptimizationGuideModelExecutionResult,
+    std::unique_ptr<optimization_guide::ModelQualityLogEntry>)>;
 }  // namespace
 
 class MockAutofillDriver : public TestAutofillDriver {
@@ -63,6 +75,10 @@
       (base::OnceCallback<void(
            std::optional<optimization_guide::proto::AnnotatedPageContent>)>),
       (override));
+  MOCK_METHOD(optimization_guide::OptimizationGuideModelExecutor*,
+              GetOptimizationGuideModelExecutor,
+              (),
+              (override));
 };
 
 class MockAmountExtractionManager : public AmountExtractionManager {
@@ -109,11 +125,13 @@
 
     test_api(payments_data()).AddBnplIssuer(test::GetTestUnlinkedBnplIssuer());
 
-    ON_CALL(
-        *static_cast<MockAutofillOptimizationGuideDecider*>(
-            autofill_manager().client().GetAutofillOptimizationGuideDecider()),
-        IsUrlEligibleForBnplIssuer)
+    ON_CALL(*static_cast<MockAutofillOptimizationGuideDecider*>(
+                autofill_client().GetAutofillOptimizationGuideDecider()),
+            IsUrlEligibleForBnplIssuer)
         .WillByDefault(Return(true));
+
+    ON_CALL(autofill_client(), GetOptimizationGuideModelExecutor())
+        .WillByDefault(Return(model_executor()));
   }
 
   TestPaymentsDataManager& payments_data() {
@@ -146,12 +164,19 @@
         .WillByDefault(std::move(extract_action));
   }
 
+  NiceMock<optimization_guide::MockOptimizationGuideModelExecutor>*
+  model_executor() {
+    return &mock_model_executor_;
+  }
+
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<AmountExtractionManager> amount_extraction_manager_;
   std::unique_ptr<MockAmountExtractionManager> mock_amount_extraction_manager_;
   ukm::TestAutoSetUkmRecorder ukm_recorder_;
+  NiceMock<optimization_guide::MockOptimizationGuideModelExecutor>
+      mock_model_executor_;
 };
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
@@ -802,6 +827,46 @@
               nullptr);
 }
 
+// Verify that if the page content is not fetched, `ExecuteModel` should not be
+// called.
+TEST_F(AmountExtractionManagerTest, ShouldNotCallExecuteModel) {
+  ASSERT_EQ(test_api(*amount_extraction_manager_).GetAiPageContent(), nullptr);
+  EXPECT_CALL(*model_executor(), ExecuteModel).Times(0);
+  amount_extraction_manager_->TriggerCheckoutAmountExtractionWithAi();
+}
+
+// Verify that when `TriggerCheckoutAmountExtractionWithAi` is called with
+// annotated page contents present, `ExecuteModel` from
+// `OptimizationGuideModelExecutor` should be invoked.
+TEST_F(AmountExtractionManagerTest, ShouldCallExecuteModel) {
+  test_api(*amount_extraction_manager_).SetAiPageContent();
+  optimization_guide::proto::AmountExtractionRequest expected_request;
+  *expected_request.mutable_annotated_page_content() =
+      optimization_guide::proto::AnnotatedPageContent();
+
+  optimization_guide::proto::AmountExtractionResponse response;
+  response.set_final_checkout_amount(123.45);
+  response.set_currency("USD");
+  response.set_is_successful(true);
+
+  EXPECT_CALL(
+      *model_executor(),
+      ExecuteModel(
+          optimization_guide::ModelBasedCapabilityKey::kAmountExtraction,
+          EqualsProto(expected_request),
+          Eq(AmountExtractionManager::kAiBasedAmountExtractionWaitTime),
+          A<ModelExecutionCallback>()))
+      .WillOnce(base::test::RunOnceCallback<3>(
+          optimization_guide::OptimizationGuideModelExecutionResult(
+              optimization_guide::AnyWrapProto(response),
+              /*execution_info=*/nullptr),
+          /*log_entry=*/nullptr));
+
+  amount_extraction_manager_->TriggerCheckoutAmountExtractionWithAi();
+
+  ASSERT_EQ(test_api(*amount_extraction_manager_).GetAiPageContent(), nullptr);
+}
+
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) ||
         // BUILDFLAG(IS_CHROMEOS)
 
diff --git a/components/autofill/core/browser/payments/full_card_request_unittest.cc b/components/autofill/core/browser/payments/full_card_request_unittest.cc
index 576fef5..a40869b1 100644
--- a/components/autofill/core/browser/payments/full_card_request_unittest.cc
+++ b/components/autofill/core/browser/payments/full_card_request_unittest.cc
@@ -43,6 +43,7 @@
 using PaymentsRpcResult = PaymentsAutofillClient::PaymentsRpcResult;
 using UnmaskCardReason = payments::PaymentsAutofillClient::UnmaskCardReason;
 
+namespace {
 // The consumer of the full card request API.
 class MockResultDelegate : public FullCardRequest::ResultDelegate {
  public:
@@ -102,6 +103,7 @@
               (const CreditCard& credit_card),
               (override));
 };
+}  // namespace
 
 // TODO(crbug.com/41412501): Simplify this test setup.
 // The test fixture for full card request.
diff --git a/components/autofill/core/browser/permissions/autofill_ai/autofill_ai_permission_utils.cc b/components/autofill/core/browser/permissions/autofill_ai/autofill_ai_permission_utils.cc
index 0f6d6162..f00c82ed 100644
--- a/components/autofill/core/browser/permissions/autofill_ai/autofill_ai_permission_utils.cc
+++ b/components/autofill/core/browser/permissions/autofill_ai/autofill_ai_permission_utils.cc
@@ -163,7 +163,8 @@
     case AutofillAiAction::kImportToWallet:
       return sync_service &&
              sync_service->GetUserSettings()->GetSelectedTypes().Has(
-                 syncer::UserSelectableType::kPayments);
+                 syncer::UserSelectableType::kPayments) &&
+             sync_service->GetActiveDataTypes().Has(syncer::AUTOFILL_VALUABLE);
     case AutofillAiAction::kIphForOptIn:
     case AutofillAiAction::kServerClassificationModel:
     case AutofillAiAction::kUseCachedServerClassificationModelResults:
diff --git a/components/autofill/core/browser/permissions/autofill_ai/autofill_ai_permission_utils_unittest.cc b/components/autofill/core/browser/permissions/autofill_ai/autofill_ai_permission_utils_unittest.cc
index 9374a770..51bf5e3a 100644
--- a/components/autofill/core/browser/permissions/autofill_ai/autofill_ai_permission_utils_unittest.cc
+++ b/components/autofill/core/browser/permissions/autofill_ai/autofill_ai_permission_utils_unittest.cc
@@ -33,6 +33,7 @@
 
 using ::base::Bucket;
 using ::base::BucketsAre;
+using ::testing::Return;
 using ::testing::Values;
 
 constexpr auto kAutofillPredictionSettingsDisable =
@@ -70,6 +71,11 @@
   NOTREACHED();
 }
 
+class MockSyncService : public syncer::TestSyncService {
+ public:
+  MOCK_CONST_METHOD0(GetActiveDataTypes, syncer::DataTypeSet());
+};
+
 // A test fixture that sets up default state so that all AutofillAI-related
 // actions are permitted.
 class AutofillAiPermissionUtilsTest : public ::testing::Test {
@@ -100,13 +106,14 @@
 
   TestAutofillClient& client() { return client_; }
   EntityDataManager& edm() { return *client().GetEntityDataManager(); }
+  MockSyncService& sync_service() { return sync_service_; }
 
  private:
   base::test::ScopedFeatureList feature_list_;
   base::test::TaskEnvironment task_environment_;
   AutofillWebDataServiceTestHelper webdata_helper_{
       std::make_unique<EntityTable>()};
-  syncer::TestSyncService sync_service_;
+  MockSyncService sync_service_;
   TestAutofillClient client_;
 };
 
@@ -117,6 +124,8 @@
   AutofillAiMayPerformActionTest() {
     client().GetSyncService()->GetUserSettings()->SetSelectedType(
         syncer::UserSelectableType::kPayments, true);
+    ON_CALL(sync_service(), GetActiveDataTypes())
+        .WillByDefault(Return(syncer::DataTypeSet{syncer::AUTOFILL_VALUABLE}));
   }
   using AutofillAiPermissionUtilsTest::AutofillAiPermissionUtilsTest;
 };
@@ -590,6 +599,16 @@
       false);
 }
 
+TEST_F(AutofillAiMayPerformActionTest,
+       ImportToWallet_FalseWhenAutofillValuableIsNotActive) {
+  ON_CALL(sync_service(), GetActiveDataTypes())
+      .WillByDefault(Return(syncer::DataTypeSet()));
+  EXPECT_EQ(
+      MayPerformAutofillAiAction(client(), AutofillAiAction::kImportToWallet,
+                                 EntityType(EntityTypeName::kVehicle)),
+      false);
+}
+
 }  // namespace
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.cc b/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.cc
index c44ae68..0d0df87 100644
--- a/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.cc
+++ b/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.cc
@@ -40,14 +40,16 @@
 
 void AutocompleteHistoryManager::OnGetSingleFieldSuggestions(
     const FormData& form,
-    const FormFieldData& field,
+    const FormStructure* form_structure,
+    const FormFieldData& trigger_field,
+    const AutofillField* trigger_autofill_field,
     const AutofillClient& client,
     SingleFieldFillRouter::OnSuggestionsReturnedCallback
         on_suggestions_returned) {
   // Cancel the pending query if there is one.
   suggestion_generator_ = nullptr;
   if (!profile_database_) {
-    std::move(on_suggestions_returned).Run(field.global_id(), {});
+    std::move(on_suggestions_returned).Run(trigger_field.global_id(), {});
     return;
   }
   suggestion_generator_ =
@@ -60,7 +62,7 @@
         std::move(callback).Run(field_id,
                                 std::move(returned_suggestions.second));
       },
-      std::move(on_suggestions_returned), field.global_id());
+      std::move(on_suggestions_returned), trigger_field.global_id());
 
   auto on_suggestion_data_returned = base::BindOnce(
       [](base::OnceCallback<void(SuggestionGenerator::ReturnedSuggestions)>
@@ -78,11 +80,11 @@
               std::move(callback));
         }
       },
-      std::move(on_suggestions_generated), form, field,
+      std::move(on_suggestions_generated), form, trigger_field,
       suggestion_generator_->GetWeakPtr());
 
   suggestion_generator_->FetchSuggestionData(
-      form, field, /*form=*/nullptr, /*field=*/nullptr, client,
+      form, trigger_field, form_structure, trigger_autofill_field, client,
       std::move(on_suggestion_data_returned));
 }
 
diff --git a/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h b/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h
index 3fefbfa..27fbe3e 100644
--- a/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h
+++ b/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h
@@ -38,17 +38,20 @@
 
   ~AutocompleteHistoryManager() override;
 
-  // Generates autocomplete suggestions for the given `field` in `form`. This is
-  // achieved through an async DB query. `client` checks if the requirements for
-  // generating autocomplete suggestions are met (e.g. autocomplete is
-  // enabled). Since autocomplete suggestions are always generated last, the
-  // `on_suggestions_returned` callback may be called with the suggestions for
-  // `field` or with an empty vector if no suggestions are available.
+  // Generates autocomplete suggestions for the given `trigger_field` in `form`.
+  // This is achieved through an async DB query. `client` checks if the
+  // requirements for generating autocomplete suggestions are met (e.g.
+  // autocomplete is enabled). Since autocomplete suggestions are always
+  // generated last, the `on_suggestions_returned` callback may be called with
+  // the suggestions for `field` or with an empty vector if no suggestions are
+  // available.
   // TODO(crbug.com/409962888): Remove this method once the new suggestion
   // generation flow is launched.
   virtual void OnGetSingleFieldSuggestions(
       const FormData& form,
-      const FormFieldData& field,
+      const FormStructure* form_structure,
+      const FormFieldData& trigger_field,
+      const AutofillField* trigger_autofill_field,
       const AutofillClient& client,
       SingleFieldFillRouter::OnSuggestionsReturnedCallback
           on_suggestions_returned);
diff --git a/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager_unittest.cc b/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager_unittest.cc
index 6f8e269..869339b 100644
--- a/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager_unittest.cc
+++ b/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager_unittest.cc
@@ -447,7 +447,9 @@
 
   // Simulate request for suggestions.
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
   run_loop.Run();
 }
 
@@ -474,7 +476,9 @@
       .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
 
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
 
   ASSERT_FALSE(db_callback.is_null());
   std::move(db_callback).Run(kTestDbQuryId, std::move(mocked_results));
@@ -500,7 +504,9 @@
       .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
   // Simulate request for suggestions.
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
   run_loop.Run();
 }
 
@@ -523,7 +529,9 @@
         .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
   // Simulate request for suggestions.
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
   run_loop.Run();
 }
 
@@ -558,7 +566,9 @@
 
   // Simulate request for suggestions.
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
 
   ASSERT_FALSE(db_callback.is_null());
   std::move(db_callback).Run(kTestDbQuryId, std::move(mocked_results));
@@ -596,7 +606,9 @@
 
   // Simulate request for suggestions.
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
 
   ASSERT_FALSE(db_callback.is_null());
   std::move(db_callback).Run(kTestDbQuryId, std::move(mocked_results));
@@ -627,7 +639,9 @@
       .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
 
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
 
   ASSERT_FALSE(db_callback.is_null());
   std::move(db_callback).Run(kTestDbQuryId, std::move(mocked_results));
@@ -657,7 +671,9 @@
   EXPECT_CALL(mock_callback, Run(test_field_.global_id(), IsEmpty()))
       .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
 
   ASSERT_FALSE(db_callback.is_null());
   std::move(db_callback).Run(kTestDbQuryId, std::move(mocked_results));
@@ -692,7 +708,9 @@
       .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
 
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
 
   ASSERT_FALSE(db_callback.is_null());
   std::move(db_callback).Run(kTestDbQuryId, std::move(mocked_results));
@@ -753,7 +771,9 @@
   MockSuggestionsReturnedCallback mock_callback;
   EXPECT_CALL(mock_callback, Run).Times(0);
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
 
   EXPECT_CALL(*web_data_service_, CancelRequest(kTestDbQuryId_first))
       .Times(1);
@@ -762,7 +782,9 @@
                                                u"SomePrefixTwo")))
       .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
 
   ASSERT_FALSE(db_callback_second.is_null());
   std::move(db_callback_second)
@@ -791,7 +813,9 @@
   EXPECT_CALL(mock_callback, Run(test_field_.global_id(), testing::IsEmpty()))
       .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
 
   // Simulate cancelling the request.
   EXPECT_CALL(*web_data_service_, CancelRequest(kTestDbQuryId));
@@ -830,7 +854,9 @@
   EXPECT_CALL(mock_callback, Run(test_field_.global_id(), testing::SizeIs(1)));
 
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
 
   ASSERT_FALSE(db_callback.is_null());
   std::move(db_callback).Run(kTestDbQuryId, std::move(mocked_results));
@@ -850,7 +876,9 @@
 
   // Simulate request for suggestions.
   autocomplete_manager_->OnGetSingleFieldSuggestions(
-      test_form_data_, test_field_, autofill_client_, mock_callback.Get());
+      test_form_data_, /*form_structure=*/nullptr, test_field_,
+      /*trigger_autofill_field=*/nullptr, autofill_client_,
+      mock_callback.Get());
   run_loop.Run();
 
   // Expect a cancel call.
diff --git a/components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.h b/components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.h
index 5b43e2f..69300cf1 100644
--- a/components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.h
+++ b/components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.h
@@ -19,7 +19,9 @@
   MOCK_METHOD(void,
               OnGetSingleFieldSuggestions,
               (const FormData& form,
+               const FormStructure* form_structure,
                const FormFieldData& field,
+               const AutofillField* autofill_field,
                const AutofillClient& client,
                SingleFieldFillRouter::OnSuggestionsReturnedCallback callback),
               (override));
diff --git a/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator.cc b/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator.cc
index 951c32cb..832efc3 100644
--- a/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator.cc
@@ -5,6 +5,8 @@
 #include "components/autofill/core/browser/suggestions/autocomplete_suggestion_generator.h"
 
 #include "base/containers/to_vector.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/data_quality/autofill_data_util.h"
 #include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/studies/autofill_experiments.h"
 #include "components/autofill/core/browser/webdata/autocomplete/autocomplete_entry.h"
@@ -69,6 +71,20 @@
     return;
   }
 
+  // Do not offer autocomplete suggestions for credit card number, cvc, and
+  // expiration date related fields. Standalone cvc fields (used to
+  // re-authenticate the use of a credit card the website has on file) will be
+  // handled separately because those have the field type
+  // CREDIT_CARD_STANDALONE_VERIFICATION_CODE.
+  if (FieldType type = trigger_autofill_field
+                           ? trigger_autofill_field->Type().GetCreditCardType()
+                           : UNKNOWN_TYPE;
+      data_util::IsCreditCardExpirationType(type) ||
+      type == CREDIT_CARD_VERIFICATION_CODE || type == CREDIT_CARD_NUMBER) {
+    std::move(callback).Run({SuggestionDataSource::kAutocomplete, {}});
+    return;
+  }
+
   CancelPendingQuery();
   if (!AutocompleteHistoryManager::IsFieldNameMeaningfulForAutocomplete(
           trigger_field.name()) ||
diff --git a/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator_unittest.cc
index c984515..06d5c1a 100644
--- a/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator_unittest.cc
@@ -7,10 +7,13 @@
 #include "base/test/mock_callback.h"
 #include "base/test/run_until.h"
 #include "base/test/task_environment.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/foundations/test_autofill_client.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
 #include "components/autofill/core/browser/webdata/autocomplete/autocomplete_entry.h"
 #include "components/autofill/core/browser/webdata/mock_autofill_webdata_service.h"
+#include "components/autofill/core/common/form_data_test_api.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -197,5 +200,31 @@
       }));
 }
 
+// Tests that AutocompleteSuggestionGenerator does not generate suggestions for
+// CreditCard-classified fields.
+TEST_F(AutocompleteSuggestionGeneratorTest,
+       CreditCardNumberField_NoSuggestions) {
+  FormData form = test::CreateTestCreditCardFormData(/*is_https=*/false,
+                                                     /*use_month_type=*/false);
+  test_api(form).field(1).set_should_autocomplete(true);
+  FormStructure form_structure(form);
+  form_structure.field(1)->SetTypeTo(AutofillType(CREDIT_CARD_NUMBER),
+                                     /*source=*/std::nullopt);
+
+  base::MockCallback<base::OnceCallback<void(
+      std::pair<SuggestionGenerator::SuggestionDataSource,
+                std::vector<SuggestionGenerator::SuggestionData>>)>>
+      suggestion_data_callback;
+
+  EXPECT_CALL(*web_data_service(), GetFormValuesForElementName).Times(0);
+  EXPECT_CALL(suggestion_data_callback,
+              Run(testing::Pair(
+                  SuggestionGenerator::SuggestionDataSource::kAutocomplete,
+                  testing::IsEmpty())));
+  generator().FetchSuggestionData(form, form.fields()[1], &form_structure,
+                                  form_structure.field(1), client(),
+                                  suggestion_data_callback.Get());
+}
+
 }  // namespace
 }  // namespace autofill
diff --git a/components/autofill/core/browser/suggestions/compose_suggestion_generator.cc b/components/autofill/core/browser/suggestions/compose_suggestion_generator.cc
index 9abfa64..725163b 100644
--- a/components/autofill/core/browser/suggestions/compose_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/compose_suggestion_generator.cc
@@ -87,8 +87,7 @@
       all_suggestion_data,
       [](const std::pair<SuggestionDataSource, std::vector<SuggestionData>>&
              data) { return !data.second.empty(); });
-  if (other_products_have_suggestion_data ||
-      IsAutofillManuallyTriggered(trigger_source_)) {
+  if (other_products_have_suggestion_data) {
     callback({FillingProduct::kCompose, {}});
     return;
   }
diff --git a/components/autofill/core/browser/webdata/account_settings/account_setting_service.cc b/components/autofill/core/browser/webdata/account_settings/account_setting_service.cc
index a91a3f0..b06ab03 100644
--- a/components/autofill/core/browser/webdata/account_settings/account_setting_service.cc
+++ b/components/autofill/core/browser/webdata/account_settings/account_setting_service.cc
@@ -18,12 +18,22 @@
 
 namespace autofill {
 
+namespace {
+constexpr std::string_view kWalletPrivacyContextualSurfacingSetting =
+    "WALLET_PRIVACY_CONTEXTUAL_SURFACING";
+}
+
 AccountSettingService::AccountSettingService(
     std::unique_ptr<AccountSettingSyncBridge> sync_bridge)
     : sync_bridge_(std::move(sync_bridge)) {}
 
 AccountSettingService::~AccountSettingService() = default;
 
+bool AccountSettingService::IsWalletPrivacyContextualSurfacingEnabled() const {
+  return GetBoolean(kWalletPrivacyContextualSurfacingSetting,
+                    /*default_value=*/false);
+}
+
 std::unique_ptr<syncer::DataTypeControllerDelegate>
 AccountSettingService::GetSyncControllerDelegate() {
   CHECK(base::FeatureList::IsEnabled(syncer::kSyncAccountSettings));
@@ -31,4 +41,17 @@
       sync_bridge_->change_processor()->GetControllerDelegate().get());
 }
 
+bool AccountSettingService::GetBoolean(std::string_view name,
+                                       bool default_value) const {
+  if (!base::FeatureList::IsEnabled(syncer::kSyncAccountSettings)) {
+    return default_value;
+  }
+  auto setting = sync_bridge_->GetSetting(name);
+  DCHECK(!setting || setting->has_bool_value());
+  if (!setting || !setting->has_bool_value()) {
+    return default_value;
+  }
+  return setting->bool_value();
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/account_settings/account_setting_service.h b/components/autofill/core/browser/webdata/account_settings/account_setting_service.h
index 045fde3..b8f68a98 100644
--- a/components/autofill/core/browser/webdata/account_settings/account_setting_service.h
+++ b/components/autofill/core/browser/webdata/account_settings/account_setting_service.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_ACCOUNT_SETTINGS_ACCOUNT_SETTING_SERVICE_H_
 
 #include <memory>
+#include <string_view>
 
 #include "components/keyed_service/core/keyed_service.h"
 
@@ -26,11 +27,26 @@
       std::unique_ptr<AccountSettingSyncBridge> bridge);
   ~AccountSettingService() override;
 
+  // Getter to check whether the user agreed to share data from Wallet to other
+  // Google services, including Chrome, in their Google account settings.
+  bool IsWalletPrivacyContextualSurfacingEnabled() const;
+
   // Returns a controller delegate for the `sync_bridge_` owned by this service.
   std::unique_ptr<syncer::DataTypeControllerDelegate>
   GetSyncControllerDelegate();
 
  private:
+  // Internals helpers to get the setting value for a given setting name by
+  // type. If no setting of the given name exists, `default_value` is returned.
+  //
+  // If a setting of the given name exists, but the type doesn't match...
+  // - a DCHECK() will fail. This is intended to catch coding errors during
+  //   development.
+  // - `default_value` is returned with DCHECKs disabled. This choice was made
+  //   to avoid that external factors lead to a crashing state, since settings
+  //   are received via the network.
+  bool GetBoolean(std::string_view name, bool default_value) const;
+
   std::unique_ptr<AccountSettingSyncBridge> sync_bridge_;
 };
 
diff --git a/components/autofill/core/browser/webdata/account_settings/account_setting_service_unittest.cc b/components/autofill/core/browser/webdata/account_settings/account_setting_service_unittest.cc
new file mode 100644
index 0000000..b5161c4
--- /dev/null
+++ b/components/autofill/core/browser/webdata/account_settings/account_setting_service_unittest.cc
@@ -0,0 +1,65 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/webdata/account_settings/account_setting_service.h"
+
+#include <vector>
+
+#include "base/functional/callback_helpers.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/autofill/core/browser/webdata/account_settings/account_setting_sync_bridge.h"
+#include "components/autofill/core/browser/webdata/account_settings/account_setting_sync_util.h"
+#include "components/sync/base/features.h"
+#include "components/sync/protocol/account_setting_specifics.pb.h"
+#include "components/sync/test/mock_data_type_local_change_processor.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+namespace {
+
+using ::testing::Return;
+
+class MockAccountSettingSyncBridge : public AccountSettingSyncBridge {
+ public:
+  using AccountSettingSyncBridge::AccountSettingSyncBridge;
+  MOCK_METHOD(std::optional<sync_pb::AccountSettingSpecifics>,
+              GetSetting,
+              (std::string_view),
+              (const, override));
+};
+
+class AccountSettingServiceTest : public testing::Test {
+ public:
+  AccountSettingServiceTest() {
+    auto bridge =
+        std::make_unique<testing::NiceMock<MockAccountSettingSyncBridge>>(
+            mock_processor_.CreateForwardingProcessor(),
+            /*store_factory=*/base::DoNothing());
+    bridge_ = static_cast<MockAccountSettingSyncBridge*>(bridge.get());
+    service_ = std::make_unique<AccountSettingService>(std::move(bridge));
+  }
+
+  AccountSettingService& service() { return *service_; }
+  MockAccountSettingSyncBridge& bridge() { return *bridge_; }
+
+ private:
+  base::test::ScopedFeatureList feature_{syncer::kSyncAccountSettings};
+  testing::NiceMock<syncer::MockDataTypeLocalChangeProcessor> mock_processor_;
+  std::unique_ptr<AccountSettingService> service_;
+  raw_ptr<MockAccountSettingSyncBridge> bridge_;  // Owned by the `service_`
+};
+
+TEST_F(AccountSettingServiceTest, GetValue) {
+  EXPECT_FALSE(service().IsWalletPrivacyContextualSurfacingEnabled());
+  ON_CALL(bridge(), GetSetting("WALLET_PRIVACY_CONTEXTUAL_SURFACING"))
+      .WillByDefault(Return(
+          CreateSettingSpecifics("WALLET_PRIVACY_CONTEXTUAL_SURFACING", true)));
+  EXPECT_TRUE(service().IsWalletPrivacyContextualSurfacingEnabled());
+}
+
+}  // namespace
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/account_settings/account_setting_sync_bridge.cc b/components/autofill/core/browser/webdata/account_settings/account_setting_sync_bridge.cc
index 3b773c6..1f94b50 100644
--- a/components/autofill/core/browser/webdata/account_settings/account_setting_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/account_settings/account_setting_sync_bridge.cc
@@ -7,66 +7,182 @@
 #include <memory>
 #include <optional>
 
+#include "base/functional/bind.h"
 #include "base/notimplemented.h"
+#include "base/notreached.h"
+#include "base/sequence_checker.h"
+#include "components/sync/base/data_type.h"
 #include "components/sync/model/client_tag_based_data_type_processor.h"
 #include "components/sync/model/data_type_store.h"
+#include "components/sync/model/in_memory_metadata_change_list.h"
+#include "components/sync/model/mutable_data_batch.h"
+#include "components/sync/protocol/account_setting_specifics.pb.h"
+#include "components/sync/protocol/entity_data.h"
 
 namespace autofill {
 
+namespace {
+
+// Macro to simplify reporting errors raised by ModelTypeStore operations.
+#define RETURN_IF_ERROR(error)               \
+  if (error) {                               \
+    change_processor()->ReportError(*error); \
+    return;                                  \
+  }
+
+std::unique_ptr<syncer::EntityData> CreateEntityData(
+    const sync_pb::AccountSettingSpecifics& specifics) {
+  auto entity = std::make_unique<syncer::EntityData>();
+  entity->name = specifics.name();
+  entity->specifics.mutable_account_setting()->CopyFrom(specifics);
+  return entity;
+}
+
+}  // namespace
+
 AccountSettingSyncBridge::AccountSettingSyncBridge(
     std::unique_ptr<syncer::DataTypeLocalChangeProcessor> change_processor,
     syncer::OnceDataTypeStoreFactory store_factory)
-    : DataTypeSyncBridge(std::move(change_processor)) {}
+    : DataTypeSyncBridge(std::move(change_processor)) {
+  std::move(store_factory)
+      .Run(syncer::ACCOUNT_SETTING,
+           base::BindOnce(&AccountSettingSyncBridge::OnStoreCreated,
+                          weak_factory_.GetWeakPtr()));
+}
+
+AccountSettingSyncBridge::~AccountSettingSyncBridge() = default;
+
+std::optional<sync_pb::AccountSettingSpecifics>
+AccountSettingSyncBridge::GetSetting(std::string_view name) const {
+  auto it = settings_.find(name);
+  if (it == settings_.end()) {
+    return std::nullopt;
+  }
+  return it->second;
+}
 
 std::unique_ptr<syncer::MetadataChangeList>
 AccountSettingSyncBridge::CreateMetadataChangeList() {
-  NOTIMPLEMENTED();
-  return nullptr;
+  return std::make_unique<syncer::InMemoryMetadataChangeList>();
 }
 
 std::optional<syncer::ModelError> AccountSettingSyncBridge::MergeFullSyncData(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
-  NOTIMPLEMENTED();
-  return std::nullopt;
+  // Since ACCOUNT_SETTING is read-only, merging local and sync data is the same
+  // as applying changes from sync locally.
+  return ApplyIncrementalSyncChanges(std::move(metadata_change_list),
+                                     std::move(entity_data));
 }
 
 std::optional<syncer::ModelError>
 AccountSettingSyncBridge::ApplyIncrementalSyncChanges(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_changes) {
-  NOTIMPLEMENTED();
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  std::unique_ptr<syncer::DataTypeStore::WriteBatch> batch =
+      store_->CreateWriteBatch();
+  batch->TakeMetadataChangesFrom(std::move(metadata_change_list));
+  for (const std::unique_ptr<syncer::EntityChange>& change : entity_changes) {
+    switch (change->type()) {
+      case syncer::EntityChange::ACTION_ADD:
+      case syncer::EntityChange::ACTION_UPDATE: {
+        const sync_pb::AccountSettingSpecifics& specifics =
+            change->data().specifics.account_setting();
+        batch->WriteData(change->storage_key(), specifics.SerializeAsString());
+        settings_.insert_or_assign(change->storage_key(), specifics);
+        break;
+      }
+      case syncer::EntityChange::ACTION_DELETE: {
+        batch->DeleteData(change->storage_key());
+        settings_.erase(change->storage_key());
+        break;
+      }
+    }
+  }
+  store_->CommitWriteBatch(
+      std::move(batch),
+      base::BindOnce(&AccountSettingSyncBridge::ReportErrorIfSet,
+                     weak_factory_.GetWeakPtr()));
   return std::nullopt;
 }
 
+void AccountSettingSyncBridge::ApplyDisableSyncChanges(
+    std::unique_ptr<syncer::MetadataChangeList> delete_metadata_change_list) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  store_->DeleteAllDataAndMetadata(base::BindOnce(
+      &AccountSettingSyncBridge::ReportErrorIfSet, weak_factory_.GetWeakPtr()));
+  settings_.clear();
+}
+
 std::unique_ptr<syncer::DataBatch> AccountSettingSyncBridge::GetDataForCommit(
     StorageKeyList storage_keys) {
-  NOTIMPLEMENTED();
-  return nullptr;
+  // ACCOUNT_SETTING is read-only, so `GetDataForCommit()` is not needed.
+  NOTREACHED();
 }
 
 std::unique_ptr<syncer::DataBatch>
 AccountSettingSyncBridge::GetAllDataForDebugging() {
-  NOTIMPLEMENTED();
-  return nullptr;
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto batch = std::make_unique<syncer::MutableDataBatch>();
+  for (const auto& [name, specifics] : settings_) {
+    batch->Put(name, CreateEntityData(specifics));
+  }
+  return batch;
 }
 
 bool AccountSettingSyncBridge::IsEntityDataValid(
     const syncer::EntityData& entity_data) const {
-  NOTIMPLEMENTED();
-  return false;
+  CHECK(entity_data.specifics.has_account_setting());
+  const sync_pb::AccountSettingSpecifics& specifics =
+      entity_data.specifics.account_setting();
+  return !specifics.name().empty();
 }
 
 std::string AccountSettingSyncBridge::GetClientTag(
     const syncer::EntityData& entity_data) const {
-  NOTIMPLEMENTED();
-  return "";
+  return GetStorageKey(entity_data);
 }
 
 std::string AccountSettingSyncBridge::GetStorageKey(
     const syncer::EntityData& entity_data) const {
-  NOTIMPLEMENTED();
-  return "";
+  return entity_data.specifics.account_setting().name();
+}
+
+void AccountSettingSyncBridge::OnStoreCreated(
+    const std::optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::DataTypeStore> store) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  RETURN_IF_ERROR(error);
+  store_ = std::move(store);
+  store_->ReadAllDataAndMetadata(
+      base::BindOnce(&AccountSettingSyncBridge::StartSyncingWithDataAndMetadata,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void AccountSettingSyncBridge::StartSyncingWithDataAndMetadata(
+    const std::optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::DataTypeStore::RecordList> data,
+    std::unique_ptr<syncer::MetadataBatch> metadata_batch) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  RETURN_IF_ERROR(error);
+  // Initialize the `settings_` with the `data`.
+  std::vector<std::pair<std::string, sync_pb::AccountSettingSpecifics>>
+      processed_entries;
+  for (const syncer::DataTypeStore::Record& record : *data) {
+    if (sync_pb::AccountSettingSpecifics specifics;
+        specifics.ParseFromString(record.value)) {
+      processed_entries.emplace_back(record.id, std::move(specifics));
+    }
+  }
+  settings_ = base::flat_map<std::string, sync_pb::AccountSettingSpecifics>(
+      std::move(processed_entries));
+  change_processor()->ModelReadyToSync(std::move(metadata_batch));
+}
+
+void AccountSettingSyncBridge::ReportErrorIfSet(
+    const std::optional<syncer::ModelError>& error) {
+  RETURN_IF_ERROR(error);
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/account_settings/account_setting_sync_bridge.h b/components/autofill/core/browser/webdata/account_settings/account_setting_sync_bridge.h
index dd327eb..17564a1a2 100644
--- a/components/autofill/core/browser/webdata/account_settings/account_setting_sync_bridge.h
+++ b/components/autofill/core/browser/webdata/account_settings/account_setting_sync_bridge.h
@@ -17,7 +17,13 @@
   explicit AccountSettingSyncBridge(
       std::unique_ptr<syncer::DataTypeLocalChangeProcessor> change_processor,
       syncer::OnceDataTypeStoreFactory store_factory);
-  ~AccountSettingSyncBridge() override = default;
+  ~AccountSettingSyncBridge() override;
+
+  // Returns the specifics for the setting of the given `name` if the bridge
+  // is aware of any such setting. Otherwise, nullopt is returned.
+  // Virtual for testing.
+  virtual std::optional<sync_pb::AccountSettingSpecifics> GetSetting(
+      std::string_view name) const;
 
   // syncer::DataTypeSyncBridge:
   std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
@@ -28,6 +34,8 @@
   std::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
       std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
       syncer::EntityChangeList entity_changes) override;
+  void ApplyDisableSyncChanges(std::unique_ptr<syncer::MetadataChangeList>
+                                   delete_metadata_change_list) override;
   std::unique_ptr<syncer::DataBatch> GetDataForCommit(
       StorageKeyList storage_keys) override;
   std::unique_ptr<syncer::DataBatch> GetAllDataForDebugging() override;
@@ -36,6 +44,31 @@
       const syncer::EntityData& entity_data) const override;
   std::string GetStorageKey(
       const syncer::EntityData& entity_data) const override;
+
+ private:
+  // Callbacks for various asynchronous operations of the `store_`.
+  void OnStoreCreated(const std::optional<syncer::ModelError>& error,
+                      std::unique_ptr<syncer::DataTypeStore> store);
+  void StartSyncingWithDataAndMetadata(
+      const std::optional<syncer::ModelError>& error,
+      std::unique_ptr<syncer::DataTypeStore::RecordList> data,
+      std::unique_ptr<syncer::MetadataBatch> metadata_batch);
+  void ReportErrorIfSet(const std::optional<syncer::ModelError>& error);
+
+  // Storage layer used by this sync bridge. Asynchronously created through the
+  // `store_factory` injected through the constructor. Non-null if creation
+  // finished without an error.
+  std::unique_ptr<syncer::DataTypeStore> store_;
+
+  // A copy of the settings from the `store_`, used for synchronous access.
+  // Keyed by `AccountSettingSpecifics::name`.
+  base::flat_map<std::string, sync_pb::AccountSettingSpecifics> settings_;
+
+  // Sequence checker ensuring that callbacks from the `store_` happen on the
+  // bridge's thread.
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<AccountSettingSyncBridge> weak_factory_{this};
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/account_settings/account_setting_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/account_settings/account_setting_sync_bridge_unittest.cc
new file mode 100644
index 0000000..7eb707df
--- /dev/null
+++ b/components/autofill/core/browser/webdata/account_settings/account_setting_sync_bridge_unittest.cc
@@ -0,0 +1,238 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/webdata/account_settings/account_setting_sync_bridge.h"
+
+#include <memory>
+
+#include "base/functional/callback_helpers.h"
+#include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "components/autofill/core/browser/webdata/account_settings/account_setting_sync_test_util.h"
+#include "components/autofill/core/browser/webdata/account_settings/account_setting_sync_util.h"
+#include "components/sync/model/data_batch.h"
+#include "components/sync/model/data_type_store.h"
+#include "components/sync/model/model_error.h"
+#include "components/sync/protocol/account_setting_specifics.pb.h"
+#include "components/sync/protocol/entity_data.h"
+#include "components/sync/test/data_type_store_test_util.h"
+#include "components/sync/test/mock_data_type_local_change_processor.h"
+#include "components/sync/test/test_matchers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+namespace {
+
+using SettingSpecifics = sync_pb::AccountSettingSpecifics;
+using ::testing::_;
+using ::testing::Optional;
+using ::testing::UnorderedElementsAre;
+
+syncer::EntityData EntityFromSpecifics(const SettingSpecifics& specifics) {
+  syncer::EntityData entity;
+  *entity.specifics.mutable_account_setting() = specifics;
+  return entity;
+}
+
+// Assumes that `batch` only contains entities with `SettingSpecifics` and
+// extracts them into a vector.
+std::vector<SettingSpecifics> ExtractSpecificsFromBatch(
+    std::unique_ptr<syncer::DataBatch> batch) {
+  std::vector<SettingSpecifics> specifics;
+  while (batch->HasNext()) {
+    std::unique_ptr<syncer::EntityData> entity = batch->Next().second;
+    CHECK(entity->specifics.has_account_setting());
+    specifics.push_back(entity->specifics.account_setting());
+  }
+  return specifics;
+}
+
+class AccountSettingSyncBridgeTest : public testing::Test {
+ public:
+  AccountSettingSyncBridgeTest()
+      : store_(syncer::DataTypeStoreTestUtil::CreateInMemoryStoreForTest()) {
+    RecreateBridgeAnsWaitForModelToSync();
+  }
+
+  template <typename... Args>
+  void RecreateBridgeAnsWaitForModelToSync(Args&&... args) {
+    // Even though the test uses an in-memory store, it still posts tasks. Wait
+    // for initialisation to complete.
+    base::RunLoop run_loop;
+    EXPECT_CALL(mock_processor(), ModelReadyToSync(std::forward<Args>(args)...))
+        .WillRepeatedly(base::test::RunClosure(run_loop.QuitClosure()));
+
+    bridge_ = std::make_unique<AccountSettingSyncBridge>(
+        mock_processor_.CreateForwardingProcessor(),
+        syncer::DataTypeStoreTestUtil::FactoryForForwardingStore(store_.get()));
+
+    run_loop.Run();
+  }
+  void RecreateBridgeAnsWaitForModelToSync() {
+    RecreateBridgeAnsWaitForModelToSync(_);
+  }
+
+  AccountSettingSyncBridge& bridge() { return *bridge_; }
+
+  syncer::DataTypeStore& store() { return *store_; }
+
+  syncer::MockDataTypeLocalChangeProcessor& mock_processor() {
+    return mock_processor_;
+  }
+
+  // Simulates starting to sync with `remote_specifics` pre-existing on the
+  // server-side. Returns true if syncing started successfully.
+  bool StartSyncingWithServerData(
+      const std::vector<SettingSpecifics>& remote_specifics) {
+    syncer::EntityChangeList change_list;
+    for (const SettingSpecifics& specifics : remote_specifics) {
+      change_list.push_back(syncer::EntityChange::CreateAdd(
+          specifics.name(), EntityFromSpecifics(specifics)));
+    }
+    return !bridge().MergeFullSyncData(bridge().CreateMetadataChangeList(),
+                                       std::move(change_list));
+  }
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<syncer::DataTypeStore> store_;
+  testing::NiceMock<syncer::MockDataTypeLocalChangeProcessor> mock_processor_;
+  std::unique_ptr<AccountSettingSyncBridge> bridge_;
+};
+
+TEST_F(AccountSettingSyncBridgeTest, GetStorageKey) {
+  syncer::EntityData entity;
+  *entity.specifics.mutable_account_setting() =
+      CreateSettingSpecifics("name", "value");
+  EXPECT_EQ(bridge().GetStorageKey(entity), "name");
+  // `GetClientTag()` is implemented using `GetStorageKey()`.
+  EXPECT_EQ(bridge().GetClientTag(entity), "name");
+}
+
+TEST_F(AccountSettingSyncBridgeTest, ModelReadyToSync_InitialSync) {
+  RecreateBridgeAnsWaitForModelToSync();
+}
+
+TEST_F(AccountSettingSyncBridgeTest, ModelReadyToSync_ExistingMetadata) {
+  // Simulate that some metadata is stored.
+  sync_pb::DataTypeState model_type_state;
+  model_type_state.set_initial_sync_state(
+      sync_pb::DataTypeState::INITIAL_SYNC_DONE);
+  auto write_batch = store().CreateWriteBatch();
+  write_batch->GetMetadataChangeList()->UpdateDataTypeState(model_type_state);
+  base::test::TestFuture<const std::optional<syncer::ModelError>&> write_result;
+  store().CommitWriteBatch(std::move(write_batch), write_result.GetCallback());
+  ASSERT_FALSE(write_result.Get());
+
+  // Expect that `ModelReadyToSync()` is called with the stored metadata when
+  // the bridge is created.
+  RecreateBridgeAnsWaitForModelToSync(syncer::MetadataBatchContains(
+      /*state=*/syncer::HasInitialSyncDone(),
+      /*entities=*/testing::IsEmpty()));
+}
+
+TEST_F(AccountSettingSyncBridgeTest, MergeFullSyncData) {
+  EXPECT_TRUE(
+      StartSyncingWithServerData({CreateSettingSpecifics("name", "value")}));
+  // Expect that the setting is available immediately.
+  EXPECT_THAT(bridge().GetSetting("name"),
+              Optional(HasStringSetting("name", "value")));
+  // Recreate the bridge, reloading from the `store()`.
+  RecreateBridgeAnsWaitForModelToSync();
+  EXPECT_THAT(bridge().GetSetting("name"),
+              Optional(HasStringSetting("name", "value")));
+}
+
+TEST_F(AccountSettingSyncBridgeTest, ApplyIncrementalSyncChanges_AddUpdate) {
+  ASSERT_TRUE(
+      StartSyncingWithServerData({CreateSettingSpecifics("name1", "string")}));
+
+  // Simulate receiving an incremental add and an update to the existing entity.
+  syncer::EntityChangeList change_list;
+  change_list.push_back(syncer::EntityChange::CreateAdd(
+      "name2", EntityFromSpecifics(CreateSettingSpecifics("name2", 123))));
+  change_list.push_back(syncer::EntityChange::CreateUpdate(
+      "name1",
+      EntityFromSpecifics(CreateSettingSpecifics("name1", "new-string"))));
+  EXPECT_FALSE(bridge().ApplyIncrementalSyncChanges(
+      bridge().CreateMetadataChangeList(), std::move(change_list)));
+
+  // Expect that the setting is available immediately.
+  EXPECT_THAT(bridge().GetSetting("name1"),
+              Optional(HasStringSetting("name1", "new-string")));
+  EXPECT_THAT(bridge().GetSetting("name2"),
+              Optional(HasIntSetting("name2", 123)));
+  // Recreate the bridge, reloading from the `store()`.
+  RecreateBridgeAnsWaitForModelToSync();
+  EXPECT_THAT(bridge().GetSetting("name1"),
+              Optional(HasStringSetting("name1", "new-string")));
+  EXPECT_THAT(bridge().GetSetting("name2"),
+              Optional(HasIntSetting("name2", 123)));
+}
+
+TEST_F(AccountSettingSyncBridgeTest, ApplyIncrementalSyncChanges_Remove) {
+  ASSERT_TRUE(
+      StartSyncingWithServerData({CreateSettingSpecifics("name", true),
+                                  CreateSettingSpecifics("name2", "string")}));
+
+  syncer::EntityChangeList change_list;
+  change_list.push_back(
+      syncer::EntityChange::CreateDelete("name1", syncer::EntityData()));
+  EXPECT_FALSE(bridge().ApplyIncrementalSyncChanges(
+      bridge().CreateMetadataChangeList(), std::move(change_list)));
+  // Expect that the change was applied immediately.
+  EXPECT_FALSE(bridge().GetSetting("name1"));
+  EXPECT_THAT(bridge().GetSetting("name2"),
+              Optional(HasStringSetting("name2", "string")));
+  // Recreate the bridge, reloading from the `store()`.
+  RecreateBridgeAnsWaitForModelToSync();
+  EXPECT_FALSE(bridge().GetSetting("name1"));
+  EXPECT_THAT(bridge().GetSetting("name2"),
+              Optional(HasStringSetting("name2", "string")));
+}
+
+TEST_F(AccountSettingSyncBridgeTest, ApplyDisableSyncChanges) {
+  ASSERT_TRUE(
+      StartSyncingWithServerData({CreateSettingSpecifics("name", "value")}));
+  ASSERT_THAT(bridge().GetSetting("name"),
+              Optional(HasStringSetting("name", "value")));
+  bridge().ApplyDisableSyncChanges(bridge().CreateMetadataChangeList());
+  // Expect that the change was applied immediately.
+  EXPECT_FALSE(bridge().GetSetting("name"));
+  // Recreate the bridge, reloading from the `store()`.
+  RecreateBridgeAnsWaitForModelToSync();
+  EXPECT_FALSE(bridge().GetSetting("name"));
+}
+
+TEST_F(AccountSettingSyncBridgeTest, GetAllDataForDebugging) {
+  ASSERT_TRUE(
+      StartSyncingWithServerData({CreateSettingSpecifics("name1", "string"),
+                                  CreateSettingSpecifics("name2", true),
+                                  CreateSettingSpecifics("name3", 123)}));
+  EXPECT_THAT(ExtractSpecificsFromBatch(bridge().GetAllDataForDebugging()),
+              UnorderedElementsAre(HasStringSetting("name1", "string"),
+                                   HasBoolSetting("name2", true),
+                                   HasIntSetting("name3", 123)));
+}
+
+TEST_F(AccountSettingSyncBridgeTest, IsEntityDataValid) {
+  SettingSpecifics specifics = CreateSettingSpecifics("name", "value");
+  EXPECT_TRUE(bridge().IsEntityDataValid(EntityFromSpecifics(specifics)));
+
+  // Specifics with an empty name are invalid.
+  specifics.mutable_name()->clear();
+  EXPECT_FALSE(bridge().IsEntityDataValid(EntityFromSpecifics(specifics)));
+
+  // Specifics with a missing name are invalid.
+  specifics.clear_name();
+  EXPECT_FALSE(bridge().IsEntityDataValid(EntityFromSpecifics(specifics)));
+}
+
+}  // namespace
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/account_settings/account_setting_sync_test_util.h b/components/autofill/core/browser/webdata/account_settings/account_setting_sync_test_util.h
new file mode 100644
index 0000000..fea7dfe
--- /dev/null
+++ b/components/autofill/core/browser/webdata/account_settings/account_setting_sync_test_util.h
@@ -0,0 +1,27 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_ACCOUNT_SETTINGS_ACCOUNT_SETTING_SYNC_TEST_UTIL_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_ACCOUNT_SETTINGS_ACCOUNT_SETTING_SYNC_TEST_UTIL_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill {
+
+// Matchers for `AccountSettingSpecifics` args with given name and values.
+MATCHER_P2(HasBoolSetting, name, value, "") {
+  return arg.name() == name && arg.has_bool_value() &&
+         arg.bool_value() == value;
+}
+MATCHER_P2(HasStringSetting, name, value, "") {
+  return arg.name() == name && arg.has_string_value() &&
+         arg.string_value() == value;
+}
+MATCHER_P2(HasIntSetting, name, value, "") {
+  return arg.name() == name && arg.has_int_value() && arg.int_value() == value;
+}
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_ACCOUNT_SETTINGS_ACCOUNT_SETTING_SYNC_TEST_UTIL_H_
diff --git a/components/autofill/core/browser/webdata/account_settings/account_setting_sync_util.cc b/components/autofill/core/browser/webdata/account_settings/account_setting_sync_util.cc
new file mode 100644
index 0000000..9e0e4e7
--- /dev/null
+++ b/components/autofill/core/browser/webdata/account_settings/account_setting_sync_util.cc
@@ -0,0 +1,28 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/webdata/account_settings/account_setting_sync_util.h"
+
+#include <string>
+#include <variant>
+
+#include "third_party/abseil-cpp/absl/functional/overload.h"
+
+namespace autofill {
+
+sync_pb::AccountSettingSpecifics CreateSettingSpecifics(
+    std::string_view name,
+    std::variant<bool, std::string_view, int64_t> value) {
+  sync_pb::AccountSettingSpecifics specifics;
+  specifics.set_name(std::string(name));
+  std::visit(
+      absl::Overload{
+          [&](bool value) { specifics.set_bool_value(value); },
+          [&](std::string_view value) { specifics.set_string_value(value); },
+          [&](int64_t value) { specifics.set_int_value(value); }},
+      value);
+  return specifics;
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/account_settings/account_setting_sync_util.h b/components/autofill/core/browser/webdata/account_settings/account_setting_sync_util.h
new file mode 100644
index 0000000..5b1d5883
--- /dev/null
+++ b/components/autofill/core/browser/webdata/account_settings/account_setting_sync_util.h
@@ -0,0 +1,21 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_ACCOUNT_SETTINGS_ACCOUNT_SETTING_SYNC_UTIL_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_ACCOUNT_SETTINGS_ACCOUNT_SETTING_SYNC_UTIL_H_
+
+#include <string_view>
+#include <variant>
+
+#include "components/sync/protocol/account_setting_specifics.pb.h"
+
+namespace autofill {
+
+sync_pb::AccountSettingSpecifics CreateSettingSpecifics(
+    std::string_view name,
+    std::variant<bool, std::string_view, int64_t> value);
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_ACCOUNT_SETTINGS_ACCOUNT_SETTING_SYNC_UTIL_H_
diff --git a/components/autofill/core/browser/webdata/addresses/address_autofill_table.cc b/components/autofill/core/browser/webdata/addresses/address_autofill_table.cc
index 769960c..0d96c025 100644
--- a/components/autofill/core/browser/webdata/addresses/address_autofill_table.cc
+++ b/components/autofill/core/browser/webdata/addresses/address_autofill_table.cc
@@ -787,13 +787,19 @@
   DCHECK(base::Uuid::ParseCaseInsensitive(profile.guid()).is_valid());
 
   // Get associated name info using guid.
-  AddLegacyAutofillProfileNamesToProfile(db(), &profile);
+  if (!AddLegacyAutofillProfileNamesToProfile(db(), &profile)) {
+    return std::nullopt;
+  }
 
   // Get associated email info using guid.
-  AddLegacyAutofillProfileEmailsToProfile(db(), &profile);
+  if (!AddLegacyAutofillProfileEmailsToProfile(db(), &profile)) {
+    return std::nullopt;
+  }
 
   // Get associated phone info using guid.
-  AddLegacyAutofillProfilePhonesToProfile(db(), &profile);
+  if (!AddLegacyAutofillProfilePhonesToProfile(db(), &profile)) {
+    return std::nullopt;
+  }
 
   // The details should be added after the other info to make sure they don't
   // change when we change the names/emails/phones.
@@ -802,7 +808,9 @@
   // The structured address information should be added after the street_address
   // from the query above was  written because this information is used to
   // detect changes by a legacy client.
-  AddLegacyAutofillProfileAddressesToProfile(db(), &profile);
+  if (!AddLegacyAutofillProfileAddressesToProfile(db(), &profile)) {
+    return std::nullopt;
+  }
 
   // For more-structured profiles, the profile must be finalized to fully
   // populate the name fields.
diff --git a/components/autofill/core/browser/webdata/addresses/contact_info_precondition_checker.cc b/components/autofill/core/browser/webdata/addresses/contact_info_precondition_checker.cc
index f393c55..2fdd8ea8 100644
--- a/components/autofill/core/browser/webdata/addresses/contact_info_precondition_checker.cc
+++ b/components/autofill/core/browser/webdata/addresses/contact_info_precondition_checker.cc
@@ -52,21 +52,21 @@
     syncer::SyncService* sync_service,
     signin::IdentityManager* identity_manager,
     base::RepeatingClosure on_precondition_changed)
-    : sync_service_(CHECK_DEREF(sync_service)),
-      identity_manager_(CHECK_DEREF(identity_manager)),
+    : identity_manager_(CHECK_DEREF(identity_manager)),
       on_precondition_changed_(std::move(on_precondition_changed)) {
-  sync_service_observation_.Observe(&sync_service_.get());
+  CHECK(sync_service);
+  sync_service_observation_.Observe(sync_service);
   // When support for Dasher users is not enabled, the managed-status of the
   // account needs to be determined.
   // Note that the controller is instantiated even when there's no signed-in
   // account.
-  CoreAccountInfo account = sync_service_->GetAccountInfo();
+  CoreAccountInfo account = GetSyncService()->GetAccountInfo();
   if (!account.IsEmpty() &&
       !base::FeatureList::IsEnabled(
           syncer::kSyncEnableContactInfoDataTypeForDasherUsers)) {
     managed_status_finder_ =
         std::make_unique<signin::AccountManagedStatusFinder>(
-            &identity_manager_.get(), sync_service_->GetAccountInfo(),
+            &identity_manager_.get(), GetSyncService()->GetAccountInfo(),
             base::BindOnce(
                 &ContactInfoPreconditionChecker::AccountTypeDetermined,
                 base::Unretained(this)));
@@ -76,8 +76,14 @@
 ContactInfoPreconditionChecker::~ContactInfoPreconditionChecker() = default;
 
 PreconditionState ContactInfoPreconditionChecker::GetPreconditionState() const {
+  const syncer::SyncService* sync_service = GetSyncService();
+  // Can happen if this gets called after `OnSyncShutdown()` - in that case,
+  // "stop and keep data" is a safe default.
+  if (!sync_service) {
+    return PreconditionState::kMustStopAndKeepData;
+  }
   // Exclude explicit passphrase users.
-  if (sync_service_->GetUserSettings()->IsUsingExplicitPassphrase() &&
+  if (sync_service->GetUserSettings()->IsUsingExplicitPassphrase() &&
       !base::FeatureList::IsEnabled(
           syncer::kSyncEnableContactInfoDataTypeForCustomPassphraseUsers)) {
     return PreconditionState::kMustStopAndClearData;
@@ -88,13 +94,14 @@
 }
 
 void ContactInfoPreconditionChecker::OnStateChanged(syncer::SyncService* sync) {
+  CHECK_EQ(sync, GetSyncService());
   // Recreate the status finder when the account has changed.
   if (!managed_status_finder_ ||
       managed_status_finder_->GetAccountInfo().account_id !=
-          sync_service_->GetAccountInfo().account_id) {
+          GetSyncService()->GetAccountInfo().account_id) {
     managed_status_finder_ =
         std::make_unique<signin::AccountManagedStatusFinder>(
-            &identity_manager_.get(), sync_service_->GetAccountInfo(),
+            &identity_manager_.get(), GetSyncService()->GetAccountInfo(),
             base::BindOnce(
                 &ContactInfoPreconditionChecker::AccountTypeDetermined,
                 base::Unretained(this)));
@@ -102,8 +109,17 @@
   on_precondition_changed_.Run();
 }
 
+void ContactInfoPreconditionChecker::OnSyncShutdown(syncer::SyncService* sync) {
+  sync_service_observation_.Reset();
+}
+
 void ContactInfoPreconditionChecker::AccountTypeDetermined() {
   on_precondition_changed_.Run();
 }
 
+const syncer::SyncService* ContactInfoPreconditionChecker::GetSyncService()
+    const {
+  return sync_service_observation_.GetSource();
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/addresses/contact_info_precondition_checker.h b/components/autofill/core/browser/webdata/addresses/contact_info_precondition_checker.h
index 9282a41..79c800a0 100644
--- a/components/autofill/core/browser/webdata/addresses/contact_info_precondition_checker.h
+++ b/components/autofill/core/browser/webdata/addresses/contact_info_precondition_checker.h
@@ -22,7 +22,8 @@
 class ContactInfoPreconditionChecker : public syncer::SyncServiceObserver {
  public:
   // `on_precondition_changed` is called whenever the result of
-  // `GetPreconditionState()` has possibly changed.
+  // `GetPreconditionState()` has possibly changed. Callers must ensure that
+  // `identity_manager` outlives this instance.
   ContactInfoPreconditionChecker(
       syncer::SyncService* sync_service,
       signin::IdentityManager* identity_manager,
@@ -33,12 +34,15 @@
 
   // SyncServiceObserver overrides.
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
  private:
   // Called by the `managed_status_finder_` when it determines the account type.
   void AccountTypeDetermined();
 
-  const raw_ref<syncer::SyncService> sync_service_;
+  // Returns the SyncService, or `nullptr` after `OnSyncShutdown()`.
+  const syncer::SyncService* GetSyncService() const;
+
   const raw_ref<signin::IdentityManager> identity_manager_;
   const base::RepeatingClosure on_precondition_changed_;
 
diff --git a/components/autofill/core/browser/webdata/valuables/valuable_sync_bridge.cc b/components/autofill/core/browser/webdata/valuables/valuable_sync_bridge.cc
index 53fda77..dd7bbac 100644
--- a/components/autofill/core/browser/webdata/valuables/valuable_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/valuables/valuable_sync_bridge.cc
@@ -8,6 +8,7 @@
 #include <optional>
 
 #include "base/check.h"
+#include "base/notreached.h"
 #include "components/autofill/core/browser/data_model/valuables/loyalty_card.h"
 #include "components/autofill/core/browser/webdata/autofill_ai/entity_sync_util.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
@@ -64,6 +65,18 @@
   return base::FeatureList::IsEnabled(syncer::kSyncWalletVehicleRegistrations);
 }
 
+// Returns if the entity `change` should be uploaded to AUTOFILL_VALUABLE.
+bool ShouldUploadEntityChange(const EntityInstanceChange& change) {
+  switch (change.data_model()->record_type()) {
+    case EntityInstance::RecordType::kLocal:
+      // Local entities are not uploaded as AUTOFILL_VALUABLE.
+      return false;
+    case EntityInstance::RecordType::kServerWallet:
+      return true;
+  }
+  NOTREACHED();
+}
+
 }  // namespace
 
 ValuableSyncBridge::ValuableSyncBridge(
@@ -452,22 +465,33 @@
 void ValuableSyncBridge::EntityInstanceChanged(
     const EntityInstanceChange& change) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Determine if the entity change should be uploaded to AUTOFILL_VALUABLE.
-  switch (change.data_model()->record_type()) {
-    case EntityInstance::RecordType::kLocal:
-      // Local entities are not uploaded as AUTOFILL_VALUABLE.
-      return;
-    case EntityInstance::RecordType::kServerWallet:
-      break;
+  if (!IsSyncWalletFlightReservationsEnabled() &&
+      !IsSyncWalletVehicleRegistrationsEnabled()) {
+    return;
   }
 
+  if (!ShouldUploadEntityChange(change)) {
+    return;
+  }
+
+  CHECK(change_processor()->IsTrackingMetadata());
+
+  std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
+      CreateMetadataChangeList();
+
   switch (change.type()) {
     case EntityInstanceChange::ADD:
     case EntityInstanceChange::UPDATE:
+      CHECK(change.data_model());
+      change_processor()->Put(
+          *change.key(),
+          CreateEntityDataFromEntityInstance(*change.data_model()),
+          metadata_change_list.get());
+      break;
     case EntityInstanceChange::REMOVE:
     case EntityInstanceChange::HIDE_IN_AUTOFILL:
-      // TODO(crbug.com/441736370) Handle switch cases.
-      break;
+      // Removing valuables is not supported from the client.
+      NOTREACHED();
   }
 }
 
diff --git a/components/autofill/core/browser/webdata/valuables/valuable_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/valuables/valuable_sync_bridge_unittest.cc
index 0c35780..1d0beb2 100644
--- a/components/autofill/core/browser/webdata/valuables/valuable_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/valuables/valuable_sync_bridge_unittest.cc
@@ -658,4 +658,55 @@
               UnorderedElementsAre(local_vehicle));
 }
 
+// Tests that `EntityInstanceChanged()` does nothing when flight and vehicle
+// sync features are disabled.
+TEST_F(ValuableSyncBridgeTest,
+       EntityInstanceChanged_DoesNothingWhenFeaturesDisabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures({}, {syncer::kSyncWalletFlightReservations,
+                                     syncer::kSyncWalletVehicleRegistrations});
+  const EntityInstance vehicle = GetServerVehicleEntityInstance();
+
+  EXPECT_CALL(mock_processor(), Put).Times(0);
+
+  bridge().EntityInstanceChanged(
+      EntityInstanceChange(EntityInstanceChange::ADD, vehicle.guid(), vehicle));
+}
+
+// Tests that `EntityInstanceChanged()` ignores local entities.
+TEST_F(ValuableSyncBridgeTest, EntityInstanceChanged_IgnoresLocalEntities) {
+  EXPECT_CALL(mock_processor(), Put).Times(0);
+  const EntityInstance vehicle = GetLocalVehicleEntityInstance();
+
+  bridge().EntityInstanceChanged(
+      EntityInstanceChange(EntityInstanceChange::ADD, vehicle.guid(), vehicle));
+}
+
+// Tests that `EntityInstanceChanged()` handles ADD and UPDATE changes.
+TEST_F(ValuableSyncBridgeTest, EntityInstanceChanged_AddUpdate) {
+  ON_CALL(mock_processor(), IsTrackingMetadata).WillByDefault(Return(true));
+  const EntityInstance vehicle = GetServerVehicleEntityInstance();
+
+  EXPECT_CALL(mock_processor(), Put(_, _, _));
+  bridge().EntityInstanceChanged(
+      EntityInstanceChange(EntityInstanceChange::ADD, vehicle.guid(), vehicle));
+
+  EXPECT_CALL(mock_processor(), Put(_, _, _));
+  bridge().EntityInstanceChanged(EntityInstanceChange(
+      EntityInstanceChange::UPDATE, vehicle.guid(), vehicle));
+}
+
+// Tests that `EntityInstanceChanged()` crashes on REMOVE change.
+TEST_F(ValuableSyncBridgeDeathTest, EntityInstanceChanged_Remove) {
+  ON_CALL(mock_processor(), IsTrackingMetadata).WillByDefault(Return(true));
+  const EntityInstance vehicle = GetServerVehicleEntityInstance();
+
+  EXPECT_DEATH_IF_SUPPORTED(
+      {
+        bridge().EntityInstanceChanged(EntityInstanceChange(
+            EntityInstanceChange::REMOVE, vehicle.guid(), vehicle));
+      },
+      ".*");
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 3d010f1c..ee6888d4 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -354,11 +354,6 @@
 BASE_FEATURE(kAutofillEnableEmailOrLoyaltyCardsFilling,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// If enabled, the Blink renderer extracts forms only on admissible URLs.
-// TODO(crbug.com/409401613): Remove after M142 branch point (2025-09-29).
-BASE_FEATURE(kAutofillExtractOnlyOnAdmissibleUrls,
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // If enabled, only non-ad frames are extracted.
 // Otherwise, non-ad frames as well as *visible* ad frames are extracted.
 // "Extracted" means that FormFieldData::child_frames is populated, which is
@@ -458,12 +453,6 @@
 BASE_FEATURE(kAutofillUseSubmittedFormInHtmlSubmission,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// If enabled, the ordering for rationalization and sectioning is the same for
-// server and heuristic predictions.
-// TODO(crbug.com/408497919): Remove when launched.
-BASE_FEATURE(kAutofillUnifyRationalizationAndSectioningOrder,
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Replaces blink::WebFormElementObserver usage in FormTracker by updated logic
 // for tracking the disappearance of forms as well as other submission
 // triggering events. See `AutofillAgent::GetSubmittedForm()` for more
@@ -484,12 +473,6 @@
 BASE_FEATURE(kAutofillReplaceFormElementObserver,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// If enabled, FormFieldData::is_visible is a heuristic for actual visibility on
-// Blink platforms.
-// Otherwise and on iOS, it's an alias for FormFieldData::is_focusable.
-// TODO(crbug.com/324199622) When abandoned, remove FormFieldData::is_visible.
-BASE_FEATURE(kAutofillDetectFieldVisibility, base::FEATURE_ENABLED_BY_DEFAULT);
-
 // If enabled, new heuristics are applied for disambiguating multiple possible
 // types in a form field. Otherwise, only the already established heuristic for
 // disambiguating address and credit card names is used.
@@ -709,13 +692,6 @@
     kAutofillEnableCacheForRegexMatchingCacheSizeParam{
         &kAutofillEnableCacheForRegexMatching, "cache_size", 1000};
 
-// If enabled, AutofillType may be populated with multiple FieldTypes where all
-// but one FieldType are Autofill AI FieldTypes.
-// This is a kill switch.
-// TODO(crbug.com/432645177): Clean up when launched.
-BASE_FEATURE(kAutofillUnionTypesForAutofillAi,
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kAutofillUKMExperimentalFields, base::FEATURE_DISABLED_BY_DEFAULT);
 const base::FeatureParam<std::string> kAutofillUKMExperimentalFieldsBucket0{
     &kAutofillUKMExperimentalFields, "autofill_experimental_regex_bucket0", ""};
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index b7d00930..7559452 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -129,7 +129,6 @@
 BASE_DECLARE_FEATURE(kAutofillCreditCardUserPerceptionSurvey);
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillAddressUserDeclinedSuggestionSurvey);
-COMPONENT_EXPORT(AUTOFILL) BASE_DECLARE_FEATURE(kAutofillDetectFieldVisibility);
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillDeduplicateAccountAddresses);
 COMPONENT_EXPORT(AUTOFILL)
@@ -162,8 +161,6 @@
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillEnableSupportForParsingWithSharedLabels);
 COMPONENT_EXPORT(AUTOFILL)
-BASE_DECLARE_FEATURE(kAutofillExtractOnlyOnAdmissibleUrls);
-COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillExtractOnlyNonAdFrames);
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillIgnoreCheckableElements);
@@ -202,8 +199,6 @@
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillUseSubmittedFormInHtmlSubmission);
 COMPONENT_EXPORT(AUTOFILL)
-BASE_DECLARE_FEATURE(kAutofillUnifyRationalizationAndSectioningOrder);
-COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillPreferSavedFormAsSubmittedForm);
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillRelaxAddressImport);
@@ -286,8 +281,6 @@
 extern const base::FeatureParam<int>
     kAutofillEnableCacheForRegexMatchingCacheSizeParam;
 COMPONENT_EXPORT(AUTOFILL)
-BASE_DECLARE_FEATURE(kAutofillUnionTypesForAutofillAi);
-COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillUKMExperimentalFields);
 COMPONENT_EXPORT(AUTOFILL)
 extern const base::FeatureParam<std::string>
diff --git a/components/autofill/core/common/autofill_util.cc b/components/autofill/core/common/autofill_util.cc
index d3adeb8..8efc781 100644
--- a/components/autofill/core/common/autofill_util.cc
+++ b/components/autofill/core/common/autofill_util.cc
@@ -176,11 +176,6 @@
   return gurl.ReplaceComponents(rep);
 }
 
-bool IsAutofillManuallyTriggered(
-    AutofillSuggestionTriggerSource trigger_source) {
-  return IsPasswordsAutofillManuallyTriggered(trigger_source).value();
-}
-
 IsPasswordRequestManuallyTriggered IsPasswordsAutofillManuallyTriggered(
     AutofillSuggestionTriggerSource trigger_source) {
   return IsPasswordRequestManuallyTriggered(
diff --git a/components/autofill/core/common/autofill_util.h b/components/autofill/core/common/autofill_util.h
index 42ec826..9ba13af 100644
--- a/components/autofill/core/common/autofill_util.h
+++ b/components/autofill/core/common/autofill_util.h
@@ -79,11 +79,6 @@
 // Strips any authentication data, as well as query and ref portions of URL.
 GURL StripAuthAndParams(const GURL& gurl);
 
-// Checks if the user triggered Autofill on a field manually through the Chrome
-// context menu.
-bool IsAutofillManuallyTriggered(
-    AutofillSuggestionTriggerSource trigger_source);
-
 // Checks if the user triggered passwords Autofill on a field manually through
 // the Chrome context menu.
 IsPasswordRequestManuallyTriggered IsPasswordsAutofillManuallyTriggered(
diff --git a/components/autofill/core/common/form_field_data.h b/components/autofill/core/common/form_field_data.h
index 92bba71..8b7604a26 100644
--- a/components/autofill/core/common/form_field_data.h
+++ b/components/autofill/core/common/form_field_data.h
@@ -507,7 +507,7 @@
   bool is_user_edited_ = false;
   CheckStatus check_status_ = CheckStatus::kNotCheckable;
   bool is_focusable_ = true;
-  bool is_visible_ = true;  // See `features::kAutofillDetectFieldVisibility`.
+  bool is_visible_ = true;
   bool should_autocomplete_ = true;
   RoleAttribute role_ = RoleAttribute::kOther;
   base::i18n::TextDirection text_direction_ = base::i18n::UNKNOWN_DIRECTION;
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index 6f26aa7..d6e3ad4 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -1038,8 +1038,8 @@
   <message name="IDS_AUTOFILL_AI_SAVE_ENTITY_DIALOG_SUBTITLE" desc="A description that appears beneath the Save title prompt. This sentence essentially defines Autofill in Chrome. We prompt the user to save their information with Chrome with the promise that Chrome can then help the user fill out forms more quickly using their saved info.">
     Save your info so that autofill can fill in more fields for you next time
   </message>
-  <message name="IDS_AUTOFILL_AI_SAVE_ENTITY_TO_WALLET_DIALOG_SUBTITLE" desc="A description that appears beneath the Save title prompt asking users whether they want to save their data to Google Wallet. This sentence essentially defines Autofill in Chrome. We prompt the user to save their information with Google Wallet with the promise that Chrome can then help the user fill out forms across devices more quickly using their saved info." translateable="false">
-    Save your info to fill out forms faster, across Google services. Your info is saved to <ph name="IDS_AUTOFILL_GOOGLE_WALLET_TITLE">$1<ex>Google Wallet</ex></ph> for <ph name="ACCOUNT">$2<ex>alexpark@gmail.com</ex></ph>
+  <message name="IDS_AUTOFILL_AI_SAVE_ENTITY_TO_WALLET_DIALOG_SUBTITLE" desc="A description that appears beneath the Save title prompt asking users whether they want to save their data to Google Wallet. This sentence essentially defines Autofill in Chrome. We prompt the user to save their information with Google Wallet with the promise that Chrome can then help the user fill out forms across devices more quickly using their saved info.">
+    Save your info to get things done faster, across Google products. Your info is saved to <ph name="IDS_AUTOFILL_GOOGLE_WALLET_TITLE">$1<ex>Google Wallet</ex></ph> for <ph name="ACCOUNT">$2<ex>alexpark@gmail.com</ex></ph>
   </message>
   <message name="IDS_AUTOFILL_AI_UPDATE_ENTITY_DIALOG_UPDATED_ATTRIBUTE_ACCESSIBLE_NAME" desc="Used in entity update dialogs, as the accessible names for rows with updated attributes">
     <ph name="ATTRIBUTE_NAME">$1<ex>Passport number</ex></ph> updated
diff --git a/components/autofill_strings_grdp/IDS_AUTOFILL_AI_SAVE_ENTITY_TO_WALLET_DIALOG_SUBTITLE.png.sha1 b/components/autofill_strings_grdp/IDS_AUTOFILL_AI_SAVE_ENTITY_TO_WALLET_DIALOG_SUBTITLE.png.sha1
new file mode 100644
index 0000000..38bf736b
--- /dev/null
+++ b/components/autofill_strings_grdp/IDS_AUTOFILL_AI_SAVE_ENTITY_TO_WALLET_DIALOG_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+59b3ceff6e770c0ad072858e0c6d360ca3512533
\ No newline at end of file
diff --git a/components/browser_sync/common_controller_builder.cc b/components/browser_sync/common_controller_builder.cc
index feed8ff..640699e 100644
--- a/components/browser_sync/common_controller_builder.cc
+++ b/components/browser_sync/common_controller_builder.cc
@@ -16,6 +16,7 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "components/autofill/core/browser/payments/autofill_wallet_data_type_controller.h"
+#include "components/autofill/core/browser/webdata/account_settings/account_setting_service.h"
 #include "components/autofill/core/browser/webdata/addresses/autofill_profile_sync_bridge.h"
 #include "components/autofill/core/browser/webdata/addresses/contact_info_data_type_controller.h"
 #include "components/autofill/core/browser/webdata/addresses/contact_info_local_data_batch_uploader.h"
@@ -202,6 +203,11 @@
 
 CommonControllerBuilder::~CommonControllerBuilder() = default;
 
+void CommonControllerBuilder::SetAccountSettingService(
+    autofill::AccountSettingService* account_setting_service) {
+  account_setting_service_.Set(account_setting_service);
+}
+
 void CommonControllerBuilder::SetAddressDataManagerGetter(
     base::RepeatingCallback<autofill::AddressDataManager*()>
         address_data_manager_getter) {
@@ -811,10 +817,18 @@
   }
 #endif
 
+#if !BUILDFLAG(IS_IOS)
   if (!disabled_types.Has(syncer::ACCOUNT_SETTING) &&
+      account_setting_service_.value() &&
       base::FeatureList::IsEnabled(syncer::kSyncAccountSettings)) {
-    // TODO(crbug.com/441735283) Complete syncing of account settings.
+    controllers.push_back(std::make_unique<DataTypeController>(
+        syncer::ACCOUNT_SETTING,
+        /*delegate_for_full_sync_mode=*/
+        account_setting_service_.value()->GetSyncControllerDelegate(),
+        /*delegate_for_transport_mode=*/
+        account_setting_service_.value()->GetSyncControllerDelegate()));
   }
+#endif
 
   if (!disabled_types.Has(syncer::SHARED_TAB_GROUP_ACCOUNT_DATA) &&
       base::FeatureList::IsEnabled(syncer::kSyncSharedTabGroupAccountData) &&
diff --git a/components/browser_sync/common_controller_builder.h b/components/browser_sync/common_controller_builder.h
index 718f67d..b3971dc 100644
--- a/components/browser_sync/common_controller_builder.h
+++ b/components/browser_sync/common_controller_builder.h
@@ -26,6 +26,7 @@
 namespace autofill {
 class AddressDataManager;
 class AutofillWebDataService;
+class AccountSettingService;
 }  // namespace autofill
 
 namespace bookmarks {
@@ -134,6 +135,8 @@
 
   // Setters to inject dependencies. Each of these setters must be invoked
   // before invoking `Build()`. In some cases it is allowed to inject nullptr.
+  void SetAccountSettingService(
+      autofill::AccountSettingService* account_setting_service);
   void SetAddressDataManagerGetter(
       base::RepeatingCallback<autofill::AddressDataManager*()>
           address_data_manager_getter);
@@ -254,6 +257,8 @@
 
   // For all above, nullopt indicates the corresponding setter wasn't invoked.
   // nullptr indicates the setter was invoked with nullptr.
+  SafeOptional<raw_ptr<autofill::AccountSettingService>>
+      account_setting_service_;
   base::RepeatingCallback<autofill::AddressDataManager*()>
       address_data_manager_getter_;
   SafeOptional<raw_ptr<signin::IdentityManager>> identity_manager_;
diff --git a/components/browser_sync/sync_internals_message_handler.cc b/components/browser_sync/sync_internals_message_handler.cc
index 0bf9b1b3..69f539b71 100644
--- a/components/browser_sync/sync_internals_message_handler.cc
+++ b/components/browser_sync/sync_internals_message_handler.cc
@@ -285,6 +285,12 @@
   SendAboutInfoAndEntityCounts();
 }
 
+void SyncInternalsMessageHandler::OnSyncShutdown(syncer::SyncService* sync) {
+  // Unreachable, since this class is tied to UI which gets destroyed before the
+  // Profile and its KeyedServices.
+  NOTREACHED();
+}
+
 void SyncInternalsMessageHandler::OnProtocolEvent(
     const syncer::ProtocolEvent& event) {
   base::Value::Dict dict = event.ToValue(include_specifics_);
diff --git a/components/browser_sync/sync_internals_message_handler.h b/components/browser_sync/sync_internals_message_handler.h
index 7e4f0a87..3d86d5d 100644
--- a/components/browser_sync/sync_internals_message_handler.h
+++ b/components/browser_sync/sync_internals_message_handler.h
@@ -142,6 +142,7 @@
 
   // syncer::SyncServiceObserver implementation.
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
   // syncer::ProtocolEventObserver implementation.
   void OnProtocolEvent(const syncer::ProtocolEvent& e) override;
diff --git a/components/browser_ui/strings/android/translations/browser_ui_strings_ar.xtb b/components/browser_ui/strings/android/translations/browser_ui_strings_ar.xtb
index 511f033..82c1bf6 100644
--- a/components/browser_ui/strings/android/translations/browser_ui_strings_ar.xtb
+++ b/components/browser_ui/strings/android/translations/browser_ui_strings_ar.xtb
@@ -7,6 +7,7 @@
 <translation id="1073417869336441572">‏ساعِدنا في تحسين متصفِّح Chrome من خلال إخبارنا بأسباب سماحك باستخدام ملفات تعريف الارتباط التابعة لجهات خارجية. <ph name="BEGIN_LINK" />إرسال ملاحظات<ph name="END_LINK" /></translation>
 <translation id="1074758928858737435">يمكنك طلب عرض المواقع الإلكترونية بنسختها المخصّصة لأجهزة الكمبيوتر</translation>
 <translation id="1085696779717592361">‏استخدام Chrome بشكلٍ تلقائي</translation>
+<translation id="113098737392899175">التنقُّل بين الصفحات باستخدام مؤشر النص عند توصيل لوحة مفاتيح</translation>
 <translation id="1178015372320901893">البطاقة السفلية "اختصاراتي"</translation>
 <translation id="1178581264944972037">الإيقاف مؤقتًا</translation>
 <translation id="1181037720776840403">إزالة</translation>
@@ -700,6 +701,7 @@
 <translation id="8803526663383843427">عندما يكون الإعداد مفعَّلاً</translation>
 <translation id="8805385115381080995">يكون التصفُّح أسرع لأنّ المواقع الإلكترونية قد لا تطلب منك إثبات أنّك مستخدِم حقيقي.</translation>
 <translation id="8816026460808729765">يمكنك حظر المواقع الإلكترونية من الوصول إلى أجهزة الاستشعار.</translation>
+<translation id="884765916894384048">التشغيل في الخلفية</translation>
 <translation id="8847988622838149491">USB</translation>
 <translation id="8874790741333031443">جرِّب السماح مؤقتًا باستخدام ملفات تعريف الارتباط التابعة لجهات خارجية. هذا يعني أنّك ستحظى بمستوى حماية أقل أثناء التصفّح، غير أنّ ميزات الموقع الإلكتروني ستعمل غالبًا على النحو المتوقَّع.</translation>
 <translation id="8889294078294184559">‏عند مواصلة التصفُّح على الإنترنت، يمكن للمواقع الإلكترونية التأكّد من أنّك مستخدِم حقيقي من خلال متصفّح Chrome وأحد المواقع الإلكترونية التي سبقت لك زيارتها.</translation>
diff --git a/components/browser_ui/strings/android/translations/browser_ui_strings_as.xtb b/components/browser_ui/strings/android/translations/browser_ui_strings_as.xtb
index 51a88e08..325cb65 100644
--- a/components/browser_ui/strings/android/translations/browser_ui_strings_as.xtb
+++ b/components/browser_ui/strings/android/translations/browser_ui_strings_as.xtb
@@ -7,6 +7,7 @@
 <translation id="1073417869336441572">আপুনি তৃতীয় পক্ষৰ কুকিসমূহক কিয় অনুমতি দিছে সেয়া আমাক জনাই আমি Chrome উন্নত কৰাত সহায় কৰক। <ph name="BEGIN_LINK" />মতামত পঠিয়াওক<ph name="END_LINK" /></translation>
 <translation id="1074758928858737435">আপুনি ছাইটসমূহৰ ডেস্কটপ সংস্কৰণ চাবলৈ ক’ব পাৰে</translation>
 <translation id="1085696779717592361">ডিফ’ল্ট হিচাপে Chrome ব্যৱহাৰ কৰক</translation>
+<translation id="113098737392899175">কোনো কীব’ৰ্ড সংলগ্ন কৰিলে পাঠৰ কাৰ্ছৰৰ জৰিয়তে পৃষ্ঠাসমূহ নেভিগে’ট কৰক</translation>
 <translation id="1178015372320901893">মোৰ শ্বৰ্টকাটৰ তলৰ শ্বীট</translation>
 <translation id="1178581264944972037">পজ কৰক</translation>
 <translation id="1181037720776840403">আঁতৰাওক</translation>
@@ -699,6 +700,7 @@
 <translation id="8803526663383843427">অন কৰা থাকিলে</translation>
 <translation id="8805385115381080995">ব্ৰাউজিং দ্ৰুত হৈছে কাৰণ এটা ছাইটে আপোনাক আপুনি এগৰাকী প্ৰকৃত ব্যক্তি বুলি সত্যাপন কৰিবলৈ কোৱাৰ সম্ভাৱনা কম</translation>
 <translation id="8816026460808729765">ছেন্সৰসমূহ এক্সেছ কৰাত ছাইটসমূহ অৱৰোধ কৰক</translation>
+<translation id="884765916894384048">পৃষ্ঠভূমিলৈ যাওক</translation>
 <translation id="8847988622838149491">USB</translation>
 <translation id="8874790741333031443">তৃতীয় পক্ষৰ কুকিসমূহক অস্থায়ীভাৱে অনুমতি দি চাওক, যাৰ ফলত ব্ৰাউজিং সুৰক্ষিত হ’ব কিন্তু ছাইটৰ সুবিধাসমূহে আশা কৰা কাম কৰাৰ সম্ভাৱনা অধিক থাকে।</translation>
 <translation id="8889294078294184559">আপুনি ব্ৰাউজ কৰি থাকিলে, ছাইটসমূহে Chromeৰ জৰিয়তে পৰীক্ষা কৰিব পাৰে আৰু আপুনি পূৰ্বে চোৱা ছাইটৰ পৰা সত্যাপন কৰিব পাৰে যে আপুনি এগৰাকী প্ৰকৃত ব্যৱহাৰকাৰী হোৱাৰ সম্ভাৱনা বেছি</translation>
diff --git a/components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb b/components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb
index 6d6ed85e..4272e58a 100644
--- a/components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb
+++ b/components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb
@@ -7,6 +7,7 @@
 <translation id="1073417869336441572">‏با گفتن دلیل مجاز کردن کوکی‌های طرف سوم، به ما در بهبود Chrome کمک کنید. <ph name="BEGIN_LINK" />ارسال بازخورد<ph name="END_LINK" /></translation>
 <translation id="1074758928858737435">می‌توانید درخواست کنید نسخه رایانه سایت‌ها را ببینید</translation>
 <translation id="1085696779717592361">‏استفاده از Chrome به‌طور پیش‌فرض</translation>
+<translation id="113098737392899175">پیمایش کردن صفحه‌ها با مکان‌نمای نوشتار هنگام اتصال صفحه‌کلید</translation>
 <translation id="1178015372320901893">برگ زیرین میان‌برهای من</translation>
 <translation id="1178581264944972037">مکث</translation>
 <translation id="1181037720776840403">برداشتن</translation>
@@ -699,6 +700,7 @@
 <translation id="8803526663383843427">وقتی روشن است</translation>
 <translation id="8805385115381080995">مرور کردن سریع‌تر می‌شود زیرا احتمال کمتری وجود دارد که سایتی از شما بخواهد تأیید کنید شما شخص واقعی هستید</translation>
 <translation id="8816026460808729765">سایت‌ها نمی‌توانند به حسگرها دسترسی داشته باشند</translation>
+<translation id="884765916894384048">رفتن به پس‌زمینه</translation>
 <translation id="8847988622838149491">USB</translation>
 <translation id="8874790741333031443">کوکی‌های طرف سوم را به‌طور موقت مجاز کنید. این کار به‌معنی محافظت کمتر هنگام مرور است اما ویژگی‌های سایت به احتمال زیاد عملکرد موردنظر را خواهد داشت.</translation>
 <translation id="8889294078294184559">‏همان‌طور که مرور می‌کنید، سایت‌ها می‌توانند ازطریق Chrome بررسی کنند و بااستفاده از سایت قبلی که بازدید کرده‌اید تأیید کنند که احتمالاً شما شخص واقعی هستید</translation>
diff --git a/components/browser_ui/strings/android/translations/browser_ui_strings_iw.xtb b/components/browser_ui/strings/android/translations/browser_ui_strings_iw.xtb
index 4f9a0d4..4fb30176 100644
--- a/components/browser_ui/strings/android/translations/browser_ui_strings_iw.xtb
+++ b/components/browser_ui/strings/android/translations/browser_ui_strings_iw.xtb
@@ -303,7 +303,7 @@
 <translation id="4226663524361240545">רטט של המכשיר אפשרי כשמתקבלת הודעה</translation>
 <translation id="4259722352634471385">הניווט חסום: <ph name="URL" /></translation>
 <translation id="4274673989874969668">לאחר יציאה מאתרים, ייתכן שפעולות סנכרון שלהם ימשיכו כדי להשלים משימות, כמו העלאת תמונות או שליחת הודעות צ'אט</translation>
-<translation id="4278390842282768270">מותר</translation>
+<translation id="4278390842282768270">יש אישור</translation>
 <translation id="4290791284969893584">לאחר סגירת דף, ייתכן שמשימות שהתחלת לא יסתיימו</translation>
 <translation id="429312253194641664">אתר מסוים מפעיל מדיה</translation>
 <translation id="42981349822642051">הרחבה</translation>
@@ -347,7 +347,7 @@
 <translation id="4816495437032298535">רמת הזום שנשמרה באתרים</translation>
 <translation id="4836046166855586901">תוצג שאלה כשאתר יבקש לדעת מתי המכשיר הזה משמש אותך באופן פעיל</translation>
 <translation id="483914009762354899">יש לכלול את כל האתרים בדומיין הזה</translation>
-<translation id="4852997963351138624">הקוד ההקסדצימלי של צבע הרקע</translation>
+<translation id="4852997963351138624">קוד הקסדצימלי של צבע הרקע</translation>
 <translation id="4882919381756638075">אתרים משתמשים בדרך כלל במיקרופון של המכשיר כדי לתמוך בתכונות תקשורת כמו וידאו צ'אט</translation>
 <translation id="4883854917563148705">לא ניתן לאפס הגדרות מנוהלות</translation>
 <translation id="4887024562049524730">תוצג שאלה לפני מתן הרשאה לאתרים להשתמש במכשיר המציאות המדומה ובנתוני המציאות המדומה שלך (מומלץ)</translation>
@@ -569,7 +569,7 @@
 <translation id="7399802613464275309">בדיקת אבטחה</translation>
 <translation id="7406113532070524618">ההגדרה הזו פועלת ללא זיהוי שלך ובלי לתת לאתרים גישה להיסטוריית הגלישה, אבל אתרים יכולים לשתף ביניהם כמות נתונים קטנה במסגרת האימות.</translation>
 <translation id="7409735910987429903">אתרים עשויים להציג חלונות קופצים עם מודעות, או להשתמש בהפניות אוטומטיות כדי להוביל אותך לאתרים שלא כדאי לבקר בהם</translation>
-<translation id="7412160840120117304">הקוד ההקסדצימלי של הצבע הראשי</translation>
+<translation id="7412160840120117304">קוד הקסדצימלי של הצבע הראשי</translation>
 <translation id="7423098979219808738">תופיע בקשת אישור</translation>
 <translation id="7423538860840206698">הגישה לקריאה מלוח העריכה נחסמה</translation>
 <translation id="7425915948813553151">עיצוב כהה באתרים</translation>
diff --git a/components/browser_ui/strings/android/translations/browser_ui_strings_ka.xtb b/components/browser_ui/strings/android/translations/browser_ui_strings_ka.xtb
index e67a3c01..a56e5d0 100644
--- a/components/browser_ui/strings/android/translations/browser_ui_strings_ka.xtb
+++ b/components/browser_ui/strings/android/translations/browser_ui_strings_ka.xtb
@@ -7,6 +7,7 @@
 <translation id="1073417869336441572">დაგვეხმარეთ, გავაუმჯობესოთ Chrome — გვითხარით, რატომ დაუშვით მესამე მხარის ქუქი-ჩანაწერები. <ph name="BEGIN_LINK" />გამოხმაურება<ph name="END_LINK" /></translation>
 <translation id="1074758928858737435">თქვენ შეგიძლიათ მოითხოვოთ საიტების დესკტოპ-ვერსიების ნახვა</translation>
 <translation id="1085696779717592361">გამოიყენეთ Chrome ნაგულისხმევად</translation>
+<translation id="113098737392899175">გადაადგილდით გვერდებზე ტექსტის კურსორით, როცა დაკავშირებულია კლავიატურა</translation>
 <translation id="1178015372320901893">ჩემი მალსახმობების ქვედა ფურცელი</translation>
 <translation id="1178581264944972037">პაუზა</translation>
 <translation id="1181037720776840403">ამოშლა</translation>
@@ -699,6 +700,7 @@
 <translation id="8803526663383843427">როცა ჩართულია</translation>
 <translation id="8805385115381080995">ვებს უფრო სწრაფად დაათვალიერებთ, რადგან ნაკლებსავარაუდოა, რომ საიტი მოგთხოვთ, დაადასტუროთ, რომ ნამდვილი ადამიანი ხართ</translation>
 <translation id="8816026460808729765">საიტებისთვის სენსორებზე წვდომის აკრძალვა</translation>
+<translation id="884765916894384048">ფონზე გადასვლა</translation>
 <translation id="8847988622838149491">USB</translation>
 <translation id="8874790741333031443">ცადეთ მესამე მხარის ქუქი-ჩანაწერების დროებით დაშვება, რაც ნიშნავს, რომ ვების დათვალიერებისას ნაკლებად იქნებით დაცული, თუმცა საიტის ფუნქციები უფრო დიდი ალბათობით იმუშავებს მოლოდინისამებრ.</translation>
 <translation id="8889294078294184559">როცა ვების დათვალიერებას გააგრძელებთ, საიტებს შეეძლება Chrome-ში წინა საიტის მიერ შენახული ინფორმაციის გადამოწმება და იმის დადასტურება, რომ ნამდვილად ადამიანი ხართ.</translation>
diff --git a/components/browser_ui/strings/android/translations/browser_ui_strings_mn.xtb b/components/browser_ui/strings/android/translations/browser_ui_strings_mn.xtb
index 6c3a4cb..7c6e5d0d 100644
--- a/components/browser_ui/strings/android/translations/browser_ui_strings_mn.xtb
+++ b/components/browser_ui/strings/android/translations/browser_ui_strings_mn.xtb
@@ -7,6 +7,7 @@
 <translation id="1073417869336441572">Та гуравдагч талын күүкинүүдийг яагаад зөвшөөрсөн талаараа бидэнд хэлснээр Chrome-г сайжруулахад туслаарай. <ph name="BEGIN_LINK" />Санал хүсэлт илгээх<ph name="END_LINK" /></translation>
 <translation id="1074758928858737435">Та сайтын компьютерын хувилбарыг харах хүсэлт гаргах боломжтой</translation>
 <translation id="1085696779717592361">Chrome-г өгөгдмөлөөр ашиглах</translation>
+<translation id="113098737392899175">Гар залгасан үед текст курсороор хуудас хооронд шилжих</translation>
 <translation id="1178015372320901893">Миний товчлолын доод хүснэгт</translation>
 <translation id="1178581264944972037">Түр зогсоох</translation>
 <translation id="1181037720776840403">Хасах</translation>
@@ -699,6 +700,7 @@
 <translation id="8803526663383843427">Асаалттай байх үед</translation>
 <translation id="8805385115381080995">Сайт танаас жинхэнэ хүн болохоо баталгаажуулахыг хүсэх магадлал багатай тул үзэхэд илүү хурдан байдаг</translation>
 <translation id="8816026460808729765">Сайтуудыг мэдрэгчид хандахыг хориглох</translation>
+<translation id="884765916894384048">Дэвсгэр лүү очих</translation>
 <translation id="8847988622838149491">USB</translation>
 <translation id="8874790741333031443">Гуравдагч талын күүкинүүдийг түр зуур зөвшөөрч үзнэ үү. Энэ нь үзэх хамгаалалт багасах хэдий ч сайтын онцлогууд тооцоолсны дагуу ажиллах магадлал өндөр байна гэсэн үг юм.</translation>
 <translation id="8889294078294184559">Таныг үргэлжлүүлэн үзэж байхад сайтууд Chrome-с шалгаж, таныг жинхэнэ хүн эсэхийг таны өмнө нь зочилсон сайтаас баталгаажуулах боломжтой</translation>
diff --git a/components/browser_ui/strings/android/translations/browser_ui_strings_vi.xtb b/components/browser_ui/strings/android/translations/browser_ui_strings_vi.xtb
index da4876e..1817ad02 100644
--- a/components/browser_ui/strings/android/translations/browser_ui_strings_vi.xtb
+++ b/components/browser_ui/strings/android/translations/browser_ui_strings_vi.xtb
@@ -7,6 +7,7 @@
 <translation id="1073417869336441572">Hãy chia sẻ lý do bạn cho phép cookie của bên thứ ba để giúp chúng tôi cải thiện Chrome. <ph name="BEGIN_LINK" />Gửi phản hồi<ph name="END_LINK" /></translation>
 <translation id="1074758928858737435">Bạn có thể yêu cầu xem phiên bản trang web dành cho máy tính</translation>
 <translation id="1085696779717592361">Sử dụng Chrome theo mặc định</translation>
+<translation id="113098737392899175">Di chuyển giữa các trang bằng con trỏ văn bản khi kết nối bàn phím</translation>
 <translation id="1178015372320901893">Bảng các lối tắt của tôi ở dưới cùng</translation>
 <translation id="1178581264944972037">Tạm dừng</translation>
 <translation id="1181037720776840403">Xoá</translation>
@@ -698,6 +699,7 @@
 <translation id="8803526663383843427">Khi bật</translation>
 <translation id="8805385115381080995">Duyệt web nhanh hơn vì trang web ít khi yêu cầu xác minh bạn là người thực hơn</translation>
 <translation id="8816026460808729765">Chặn không cho các trang web sử dụng cảm biến</translation>
+<translation id="884765916894384048">Chuyển sang phát trong nền</translation>
 <translation id="8847988622838149491">USB</translation>
 <translation id="8874790741333031443">Hãy thử tạm thời cho phép cookie của bên thứ ba. Chế độ cài đặt này sẽ làm giảm khả năng bảo vệ khi duyệt web nhưng sẽ tăng khả năng các tính năng của trang web hoạt động như dự kiến.</translation>
 <translation id="8889294078294184559">Khi bạn tiếp tục duyệt web, các trang web có thể kiểm tra qua Chrome và xác minh qua trang web mà bạn truy cập trước đó rằng bạn có thể là người thực</translation>
diff --git a/components/browser_ui/strings/android/translations/browser_ui_strings_zh-HK.xtb b/components/browser_ui/strings/android/translations/browser_ui_strings_zh-HK.xtb
index b249e218d..dedcf1d9 100644
--- a/components/browser_ui/strings/android/translations/browser_ui_strings_zh-HK.xtb
+++ b/components/browser_ui/strings/android/translations/browser_ui_strings_zh-HK.xtb
@@ -7,6 +7,7 @@
 <translation id="1073417869336441572">請說明你允許使用第三方 Cookie 的原因,協助我們改善 Chrome。<ph name="BEGIN_LINK" />傳送意見<ph name="END_LINK" /></translation>
 <translation id="1074758928858737435">你可要求查看桌面版網站</translation>
 <translation id="1085696779717592361">預設為使用 Chrome</translation>
+<translation id="113098737392899175">接駁鍵盤後,使用文字游標瀏覽頁面</translation>
 <translation id="1178015372320901893">我嘅捷徑頁底面板</translation>
 <translation id="1178581264944972037">暫停</translation>
 <translation id="1181037720776840403">移除</translation>
@@ -699,6 +700,7 @@
 <translation id="8803526663383843427">開啟時</translation>
 <translation id="8805385115381080995">降低網站要求驗證您是真人的機率,讓瀏覽體驗更快速</translation>
 <translation id="8816026460808729765">禁止網站存取感應器</translation>
+<translation id="884765916894384048">轉為背景播放</translation>
 <translation id="8847988622838149491">USB</translation>
 <translation id="8874790741333031443">請嘗試暫時允許使用第三方 Cookie,這代表瀏覽保護程度較低,但網站功能較容易如期運作。</translation>
 <translation id="8889294078294184559">您隨後瀏覽的網站可從 Chrome 取得前一個網站提供的資料,確認您應該是真人</translation>
diff --git a/components/browser_ui/widget/android/java/res/drawable-v31/dialog_bg_tinted.xml b/components/browser_ui/widget/android/java/res/drawable-v31/dialog_bg_tinted.xml
index 9f5136bf..f9da4bd 100644
--- a/components/browser_ui/widget/android/java/res/drawable-v31/dialog_bg_tinted.xml
+++ b/components/browser_ui/widget/android/java/res/drawable-v31/dialog_bg_tinted.xml
@@ -5,14 +5,7 @@
 found in the LICENSE file.
 -->
 
-<layer-list
-    xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/popup_bg" />
-    <item>
-        <shape
-            android:shape="rectangle">
-            <solid android:color="@color/dialog_bg_color"/>
-            <corners android:radius="@dimen/popup_bg_corner_radius" />
-        </shape>
-    </item>
-</layer-list>
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/popup_bg"
+    android:tint="@color/dialog_bg_color"
+    android:tintMode="multiply"/>
diff --git a/components/browser_ui/widget/android/java/res/layout/empty_state_view.xml b/components/browser_ui/widget/android/java/res/layout/empty_state_view.xml
index ca1aac87..8deaa15f 100644
--- a/components/browser_ui/widget/android/java/res/layout/empty_state_view.xml
+++ b/components/browser_ui/widget/android/java/res/layout/empty_state_view.xml
@@ -9,7 +9,7 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/empty_state_container"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="match_parent"
     android:paddingBottom="@dimen/selectable_list_toolbar_height"
     android:clipToPadding="false"
     android:layout_marginEnd="@dimen/default_list_row_padding"
diff --git a/components/browsing_topics/annotator_fuzzer.cc b/components/browsing_topics/annotator_fuzzer.cc
index 89196e4..624ff82d 100644
--- a/components/browsing_topics/annotator_fuzzer.cc
+++ b/components/browsing_topics/annotator_fuzzer.cc
@@ -31,6 +31,7 @@
   void AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OptimizationTarget optimization_target,
       const std::optional<optimization_guide::proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       optimization_guide::OptimizationTargetModelObserver* observer) override {
     optimization_guide::proto::Any any_metadata;
     any_metadata.set_type_url(
diff --git a/components/browsing_topics/annotator_impl_unittest.cc b/components/browsing_topics/annotator_impl_unittest.cc
index c95bb216..d1012ce3 100644
--- a/components/browsing_topics/annotator_impl_unittest.cc
+++ b/components/browsing_topics/annotator_impl_unittest.cc
@@ -43,6 +43,7 @@
   void AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OptimizationTarget target,
       const std::optional<optimization_guide::proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       optimization_guide::OptimizationTargetModelObserver* observer) override {
     registered_model_metadata_.insert_or_assign(target, model_metadata);
   }
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 1436ad7e7..08fe090 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
 {
-  "version": "69.15",
-  "log_list_timestamp": "2025-10-02T12:53:10Z",
+  "version": "69.19",
+  "log_list_timestamp": "2025-10-06T12:58:37Z",
   "operators": [
     {
       "name": "Google",
diff --git a/components/commerce/core/webui/product_specifications_handler.cc b/components/commerce/core/webui/product_specifications_handler.cc
index 30f3521f..8362e27 100644
--- a/components/commerce/core/webui/product_specifications_handler.cc
+++ b/components/commerce/core/webui/product_specifications_handler.cc
@@ -173,4 +173,10 @@
   }
 }
 
+void ProductSpecificationsHandler::OnSyncShutdown(syncer::SyncService* sync) {
+  // Unreachable, since this class is tied to UI which gets destroyed before the
+  // Profile and its KeyedServices.
+  NOTREACHED();
+}
+
 }  // namespace commerce
diff --git a/components/commerce/core/webui/product_specifications_handler.h b/components/commerce/core/webui/product_specifications_handler.h
index a9ae38c..7539ab6 100644
--- a/components/commerce/core/webui/product_specifications_handler.h
+++ b/components/commerce/core/webui/product_specifications_handler.h
@@ -114,6 +114,7 @@
 
   // syncer::SyncServiceObserver impl:
   void OnStateChanged(syncer::SyncService* sync) override;
+  void OnSyncShutdown(syncer::SyncService* sync) override;
 
  private:
   mojo::Remote<product_specifications::mojom::Page> remote_page_;
diff --git a/components/content_settings/content/content_settings_partition_key_utils.cc b/components/content_settings/content/content_settings_partition_key_utils.cc
index d5b1f69..3a8fbc6 100644
--- a/components/content_settings/content/content_settings_partition_key_utils.cc
+++ b/components/content_settings/content/content_settings_partition_key_utils.cc
@@ -13,11 +13,6 @@
 namespace content_settings {
 
 PartitionKey GetPartitionKey(const content::StoragePartitionConfig& config) {
-  if (base::FeatureList::IsEnabled(
-          content_settings::features::kContentSettingsPartitioning)) {
-    return PartitionKey(config.partition_domain(), config.partition_name(),
-                        config.in_memory());
-  }
   return PartitionKey();
 }
 
diff --git a/components/content_settings/core/browser/content_settings_registry.cc b/components/content_settings/core/browser/content_settings_registry.cc
index 4766cd8..4853f8d0 100644
--- a/components/content_settings/core/browser/content_settings_registry.cc
+++ b/components/content_settings/core/browser/content_settings_registry.cc
@@ -334,17 +334,6 @@
            ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
            PermissionSettingsInfo::EXCEPTIONS_ON_SECURE_AND_INSECURE_ORIGINS);
 
-  Register(ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL,
-           "top-level-3pcd-origin-trial", CONTENT_SETTING_ALLOW,
-           WebsiteSettingsInfo::UNSYNCABLE,
-           /*allowlisted_primary_schemes=*/{},
-           /*valid_settings=*/{CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK},
-           WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE,
-           WebsiteSettingsRegistry::DESKTOP |
-               WebsiteSettingsRegistry::PLATFORM_ANDROID,
-           ContentSettingsInfo::DONT_INHERIT_IN_INCOGNITO,
-           PermissionSettingsInfo::EXCEPTIONS_ON_SECURE_AND_INSECURE_ORIGINS);
-
   Register(ContentSettingsType::STORAGE_ACCESS_HEADER_ORIGIN_TRIAL,
            "storage-access-header-origin-trial", CONTENT_SETTING_BLOCK,
            WebsiteSettingsInfo::UNSYNCABLE,
diff --git a/components/content_settings/core/browser/content_settings_registry_unittest.cc b/components/content_settings/core/browser/content_settings_registry_unittest.cc
index 0e75c262..3dea4a05 100644
--- a/components/content_settings/core/browser/content_settings_registry_unittest.cc
+++ b/components/content_settings/core/browser/content_settings_registry_unittest.cc
@@ -153,15 +153,6 @@
     // they should be marked as INHERIT_IN_INCOGNITO.
     if (info->IsSettingValid(CONTENT_SETTING_ALLOW) &&
         info->GetInitialDefaultSetting() == CONTENT_SETTING_ALLOW) {
-      // Top-level 3pcd origin trial content settings are a special case that
-      // should not be inherited in incognito, despite being ALLOW-by-default.
-      if (info->website_settings_info()->type() ==
-          ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL) {
-        EXPECT_EQ(info->incognito_behavior(),
-                  ContentSettingsInfo::DONT_INHERIT_IN_INCOGNITO);
-        continue;
-      }
-
       EXPECT_EQ(info->incognito_behavior(),
                 ContentSettingsInfo::INHERIT_IN_INCOGNITO);
       continue;
diff --git a/components/content_settings/core/browser/content_settings_uma_util.cc b/components/content_settings/core/browser/content_settings_uma_util.cc
index 96a383d6..1023711 100644
--- a/components/content_settings/core/browser/content_settings_uma_util.cc
+++ b/components/content_settings/core/browser/content_settings_uma_util.cc
@@ -142,7 +142,7 @@
     {ContentSettingsType::POINTER_LOCK, 121},
     {ContentSettingsType::REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS, 122},
     {ContentSettingsType::TRACKING_PROTECTION, 123},
-    {ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL, 124},
+    // Removed TOP_LEVEL_TPCD_ORIGIN_TRIAL in M143.
     {ContentSettingsType::DISPLAY_MEDIA_SYSTEM_AUDIO, 125},
     {ContentSettingsType::JAVASCRIPT_OPTIMIZER, 126},
     {ContentSettingsType::STORAGE_ACCESS_HEADER_ORIGIN_TRIAL, 127},
diff --git a/components/content_settings/core/browser/cookie_settings_unittest.cc b/components/content_settings/core/browser/cookie_settings_unittest.cc
index df02041..c55d0e8d 100644
--- a/components/content_settings/core/browser/cookie_settings_unittest.cc
+++ b/components/content_settings/core/browser/cookie_settings_unittest.cc
@@ -2383,102 +2383,4 @@
 
 #endif
 
-#if !BUILDFLAG(IS_IOS)
-class CookieSettingsTopLevelTpcdOriginTrialTest
-    : public CookieSettingsTestBase {
- public:
-  CookieSettingsTopLevelTpcdOriginTrialTest() {
-    std::vector<base::test::FeatureRef> enabled_features;
-    std::vector<base::test::FeatureRef> disabled_features;
-
-    enabled_features.push_back(net::features::kTpcdMetadataGrants);
-    enabled_features.push_back(net::features::kTopLevelTpcdOriginTrial);
-
-    feature_list_.InitWithFeatures(enabled_features, disabled_features);
-  }
-
-  void AddSettingForTopLevel3pcdOriginTrial(GURL top_level_url,
-                                            ContentSetting setting) {
-    // Top-level 3pcd origin trial settings use
-    // `WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE` by default and as a result
-    // only use a primary pattern (with wildcard placeholder for the secondary
-    // pattern).
-    settings_map_->SetContentSettingDefaultScope(
-        top_level_url, GURL(), ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL,
-        CONTENT_SETTING_BLOCK);
-  }
-};
-
-TEST_F(CookieSettingsTopLevelTpcdOriginTrialTest,
-       GetCookieSetting3pcdOriginTrial) {
-  const GURL top_level_url(kFirstPartySite);
-  const GURL url(kAllowedSite);
-  const GURL third_url(kBlockedSite);
-
-  base::HistogramTester histogram_tester;
-  histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0);
-
-  prefs_.SetBoolean(prefs::kTrackingProtection3pcdEnabled, false);
-
-  // Verify third-party cookie access is blocked by a Top-level 3PCD origin
-  // trial setting.
-  AddSettingForTopLevel3pcdOriginTrial(top_level_url, CONTENT_SETTING_BLOCK);
-  EXPECT_EQ(cookie_settings_->GetCookieSetting(
-                url, net::SiteForCookies(), top_level_url,
-                net::CookieSettingOverrides(), nullptr),
-            CONTENT_SETTING_BLOCK);
-  histogram_tester.ExpectUniqueSample(
-      kAllowedRequestsHistogram,
-      static_cast<int>(net::cookie_util::StorageAccessResult::ACCESS_BLOCKED),
-      1);
-
-  // Add a mitigation setting (e.g., 3PCD metadata grant) to unblock third-party
-  // cookies.
-  ContentSettingsForOneType tpcd_metadata_grants;
-  tpcd_metadata_grants.emplace_back(
-      ContentSettingsPattern::FromURLNoWildcard(url),
-      ContentSettingsPattern::FromURLNoWildcard(top_level_url),
-      base::Value(ContentSetting::CONTENT_SETTING_ALLOW),
-      content_settings::ProviderType::kNone, false);
-  tpcd_metadata_manager_->SetGrantsForTesting(tpcd_metadata_grants);
-
-  // Verify the mitigation setting unblocks cookies.
-  EXPECT_EQ(cookie_settings_->GetCookieSetting(
-                url, net::SiteForCookies(), top_level_url,
-                net::CookieSettingOverrides(), nullptr),
-            CONTENT_SETTING_ALLOW);
-  histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 2);
-  histogram_tester.ExpectBucketCount(
-      kAllowedRequestsHistogram,
-      static_cast<int>(net::cookie_util::StorageAccessResult::
-                           ACCESS_ALLOWED_3PCD_METADATA_GRANT),
-      1);
-
-  // Check override mitigation setting.
-  net::CookieSettingOverrides overrides;
-  overrides.Put(net::CookieSettingOverride::kSkipTPCDMetadataGrant);
-  EXPECT_EQ(cookie_settings_->GetCookieSetting(
-                url, net::SiteForCookies(), top_level_url, overrides, nullptr),
-            CONTENT_SETTING_BLOCK);
-
-  // Invalid pair the `top_level_url` granting access to `url` is now being
-  // loaded under `url` as the top level url.
-  EXPECT_EQ(cookie_settings_->GetCookieSetting(
-                top_level_url, net::SiteForCookies(), url,
-                net::CookieSettingOverrides(), nullptr),
-            CONTENT_SETTING_ALLOW);
-
-  // Invalid pairs where a `third_url` is used as the top-level url.
-  EXPECT_EQ(cookie_settings_->GetCookieSetting(
-                url, net::SiteForCookies(), third_url,
-                net::CookieSettingOverrides(), nullptr),
-            CONTENT_SETTING_ALLOW);
-  EXPECT_EQ(cookie_settings_->GetCookieSetting(
-                top_level_url, net::SiteForCookies(), third_url,
-                net::CookieSettingOverrides(), nullptr),
-            CONTENT_SETTING_ALLOW);
-}
-
-#endif
-
 }  // namespace content_settings
diff --git a/components/content_settings/core/common/content_settings_types.mojom b/components/content_settings/core/common/content_settings_types.mojom
index 9eece79..1a476a6aa 100644
--- a/components/content_settings/core/common/content_settings_types.mojom
+++ b/components/content_settings/core/common/content_settings_types.mojom
@@ -363,13 +363,6 @@
   // requesting-origin/top-level-site combination and persistent.
   TOP_LEVEL_TPCD_TRIAL,
 
-  // Content Setting for a first-party origin trial that allows websites to
-  // enable third-party cookie deprecation.
-  // ALLOW (default): no effect (e.g. third-party cookies allowed, if not
-  //                  blocked otherwise).
-  // BLOCK: third-party cookies blocked, but 3PCD mitigations enabled.
-  TOP_LEVEL_TPCD_ORIGIN_TRIAL,
-
   // Content setting used to indicate whether entering picture-in-picture
   // automatically should be enabled.
   AUTO_PICTURE_IN_PICTURE,
diff --git a/components/content_settings/core/common/cookie_settings_base.cc b/components/content_settings/core/common/cookie_settings_base.cc
index 11049c5..351d509 100644
--- a/components/content_settings/core/common/cookie_settings_base.cc
+++ b/components/content_settings/core/common/cookie_settings_base.cc
@@ -195,7 +195,6 @@
           ContentSettingsType::TOP_LEVEL_TPCD_TRIAL,
           ContentSettingsType::FEDERATED_IDENTITY_SHARING,
           ContentSettingsType::TRACKING_PROTECTION,
-          ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL,
           ContentSettingsType::LEGACY_COOKIE_SCOPE,
       });
   return kInstance;
@@ -472,7 +471,6 @@
 }
 
 bool CookieSettingsBase::ShouldConsiderMitigationsFor3pcd(
-    const GURL& first_party_url,
     net::CookieSettingOverrides overrides) const {
   // Mitigations should take effect if they are enabled (through means such as
   // 3PCD or forced 3PC phaseout) or if third-party cookies are not blocked
@@ -480,23 +478,7 @@
   // under `first_party_url` .
   return overrides.Has(net::CookieSettingOverride::
                            kForceEnableThirdPartyCookieMitigations) ||
-         MitigationsEnabledFor3pcd() ||
-         (!ShouldBlockThirdPartyCookies(/*top_frame_origin=*/std::nullopt,
-                                        overrides) &&
-          IsBlockedByTopLevel3pcdOriginTrial(first_party_url));
-}
-
-bool CookieSettingsBase::IsBlockedByTopLevel3pcdOriginTrial(
-    const GURL& first_party_url) const {
-#if BUILDFLAG(IS_IOS)
-  return false;
-#else
-  return base::FeatureList::IsEnabled(
-             net::features::kTopLevelTpcdOriginTrial) &&
-         GetContentSetting(first_party_url, first_party_url,
-                           ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL,
-                           /*info=*/nullptr) == CONTENT_SETTING_BLOCK;
-#endif
+         MitigationsEnabledFor3pcd();
 }
 
 bool CookieSettingsBase::IsAllowedBy3pcdTrialSettings(
@@ -505,7 +487,7 @@
     net::CookieSettingOverrides overrides) const {
   return base::FeatureList::IsEnabled(net::features::kTpcdTrialSettings) &&
          !overrides.Has(net::CookieSettingOverride::kSkipTPCDTrial) &&
-         ShouldConsiderMitigationsFor3pcd(first_party_url, overrides) &&
+         ShouldConsiderMitigationsFor3pcd(overrides) &&
          GetContentSetting(url, first_party_url,
                            ContentSettingsType::TPCD_TRIAL,
                            /*info=*/nullptr) == CONTENT_SETTING_ALLOW;
@@ -517,7 +499,7 @@
   return base::FeatureList::IsEnabled(
              net::features::kTopLevelTpcdTrialSettings) &&
          !overrides.Has(net::CookieSettingOverride::kSkipTopLevelTPCDTrial) &&
-         ShouldConsiderMitigationsFor3pcd(first_party_url, overrides) &&
+         ShouldConsiderMitigationsFor3pcd(overrides) &&
          // Top-level 3pcd trial settings use
          // |WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE| by default and as a
          // result only use a primary pattern (with wildcard placeholder for the
@@ -544,10 +526,6 @@
           net::CookieSettingOverride::kForceEnableThirdPartyCookies)) {
     return ModifierMode::kAllow;
   }
-  if (top_frame_origin &&
-      IsBlockedByTopLevel3pcdOriginTrial(top_frame_origin->GetURL())) {
-    return ModifierMode::kPhaseout;
-  }
   return ModifierMode::kUndefined;
 }
 
@@ -570,7 +548,7 @@
     net::CookieSettingOverrides overrides) const {
   return base::FeatureList::IsEnabled(net::features::kTpcdMetadataGrants) &&
          !overrides.Has(net::CookieSettingOverride::kSkipTPCDMetadataGrant) &&
-         ShouldConsiderMitigationsFor3pcd(first_party_url, overrides);
+         ShouldConsiderMitigationsFor3pcd(overrides);
 }
 
 CookieSettingsBase::IsAllowedWithMetadata
@@ -609,7 +587,7 @@
              content_settings::features::kTpcdHeuristicsGrants) &&
          features::kTpcdReadHeuristicsGrants.Get() &&
          !overrides.Has(net::CookieSettingOverride::kSkipTPCDHeuristicsGrant) &&
-         ShouldConsiderMitigationsFor3pcd(first_party_url, overrides) &&
+         ShouldConsiderMitigationsFor3pcd(overrides) &&
          GetContentSetting(url, first_party_url,
                            ContentSettingsType::TPCD_HEURISTICS_GRANTS,
                            /*info=*/nullptr) == CONTENT_SETTING_ALLOW;
diff --git a/components/content_settings/core/common/cookie_settings_base.h b/components/content_settings/core/common/cookie_settings_base.h
index 8cca399..4dc3f9f 100644
--- a/components/content_settings/core/common/cookie_settings_base.h
+++ b/components/content_settings/core/common/cookie_settings_base.h
@@ -259,7 +259,7 @@
   };
 
   // Set of types relevant for CookieSettings.
-  using CookieSettingsTypeSet = base::fixed_flat_set<ContentSettingsType, 11>;
+  using CookieSettingsTypeSet = base::fixed_flat_set<ContentSettingsType, 10>;
 
   // ContentSettings listed in this set will be automatically synced to the
   // CookieSettings instance in the network service.
@@ -431,13 +431,8 @@
       const GURL& first_party_url,
       net::CookieSettingOverrides overrides) const;
 
-  // Returns true if there is a settings for the origin trial for third-party
-  // cookie deprecation blocking third-party cookie access under
-  // `first_party_url`.
-  bool IsBlockedByTopLevel3pcdOriginTrial(const GURL& first_party_url) const;
-
   // The cookie behavior that may result from a cookie settings modifier
-  // (`CookieSettingOverrides` or origin trial).
+  // (`CookieSettingOverrides`).
   enum class ModifierMode {
     // Indicates that the modifiers are not enough to determine the resulting
     // cookie behavior.
@@ -454,14 +449,14 @@
     kBlock = 3,
   };
 
-  // Will return the `ModifierMode` based on the `CookieSettingOverrides` and
-  // top-level 3pcd origin trial status.
+  // Will return the `ModifierMode` based on the `CookieSettingOverrides`
+  // status.
   ModifierMode GetModifierMode(
       base::optional_ref<const url::Origin> top_frame_origin,
       net::CookieSettingOverrides overrides) const;
 
   // Returns whether third-party cookies should be blocked solely due to
-  // third-party-cookie "modifiers" (`CookieSettingOverrides` or origin trial).
+  // third-party-cookie "modifiers" (`CookieSettingOverrides`).
   // If the modifiers are not enough to determine a decision, `std::nullopt`
   // will be returned.
   std::optional<bool> MaybeBlockThirdPartyCookiesPerModifiers(
@@ -558,11 +553,9 @@
       net::CookieSettingOverrides overrides) const = 0;
 
   // Returns whether Third Party Cookie Deprecation mitigations should take
-  // effect (under `first_party_url`). True when mitigations are enabled for
-  // 3PCD or when third-party cookies are not blocked and the origin trial for
-  // 3PCD is enabled for `first_party_url`.
+  // effect. True when mitigations are enabled for
+  // 3PCD.
   bool ShouldConsiderMitigationsFor3pcd(
-      const GURL& first_party_url,
       net::CookieSettingOverrides overrides) const;
   // Returns whether Third Party Cookie Deprecation mitigations are enabled,
   // which requires that we are not blocking or allowing all 3PC and that either
diff --git a/components/content_settings/core/common/cookie_settings_base_unittest.cc b/components/content_settings/core/common/cookie_settings_base_unittest.cc
index 70307ca..0de9d162 100644
--- a/components/content_settings/core/common/cookie_settings_base_unittest.cc
+++ b/components/content_settings/core/common/cookie_settings_base_unittest.cc
@@ -47,8 +47,8 @@
   explicit CallbackCookieSettings(GetSettingCallback callback)
       : callback_(std::move(callback)) {}
 
-  // A simple constructor that returns a specified setting for COOKIES, ALLOW
-  // for TOP_LEVEL_TPCD_ORIGIN_TRIAL, and BLOCK otherwise.
+  // A simple constructor that returns the specified `setting` for COOKIES and
+  // BLOCK otherwise.
   explicit CallbackCookieSettings(ContentSetting setting)
       : callback_(base::BindLambdaForTesting(
             [setting](const GURL&, ContentSettingsType type, SettingInfo*) {
@@ -56,10 +56,6 @@
                 return setting;
               }
 
-              if (type == ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL) {
-                return CONTENT_SETTING_ALLOW;
-              }
-
               return CONTENT_SETTING_BLOCK;
             })) {}
 
diff --git a/components/content_settings/core/common/features.cc b/components/content_settings/core/common/features.cc
index 68e0dfd0..2e200fd 100644
--- a/components/content_settings/core/common/features.cc
+++ b/components/content_settings/core/common/features.cc
@@ -156,8 +156,6 @@
         kTpcdPopupHeuristicEnableForIframeInitiatorName,
         EnableForIframeTypes::kAll, &kEnableForIframeTypesOptions};
 
-BASE_FEATURE(kContentSettingsPartitioning, base::FEATURE_DISABLED_BY_DEFAULT);
-
 BASE_FEATURE(kForceAllowStorageAccess, base::FEATURE_DISABLED_BY_DEFAULT);
 
 }  // namespace features
diff --git a/components/content_settings/core/common/features.h b/components/content_settings/core/common/features.h
index 1cd3b34..8785e55 100644
--- a/components/content_settings/core/common/features.h
+++ b/components/content_settings/core/common/features.h
@@ -249,11 +249,6 @@
 // End of third-party cookie access heuristics features //
 //////////////////////////////////////////////////////////
 
-// Whether we should partition content settings (by StoragePartitions for
-// non-ios platforms).
-COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
-BASE_DECLARE_FEATURE(kContentSettingsPartitioning);
-
 COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
 extern const char kUseTestMetadataName[];
 
diff --git a/components/device_signals/core/browser/android/android_os_signals_collector.cc b/components/device_signals/core/browser/android/android_os_signals_collector.cc
index e30395aa..35da5d2 100644
--- a/components/device_signals/core/browser/android/android_os_signals_collector.cc
+++ b/components/device_signals/core/browser/android/android_os_signals_collector.cc
@@ -7,6 +7,8 @@
 #include <utility>
 
 #include "base/functional/bind.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/stringprintf.h"
 #include "components/device_signals/core/browser/signals_types.h"
 #include "components/device_signals/core/browser/user_permission_service.h"
 #include "components/device_signals/core/common/common_types.h"
@@ -15,12 +17,29 @@
 #include "components/safe_browsing/android/safe_browsing_api_handler_bridge.h"
 #include "components/safe_browsing/android/safe_browsing_api_handler_util.h"
 
+using safe_browsing::HasHarmfulAppsResultStatus;
 using safe_browsing::SafeBrowsingApiHandlerBridge;
 using safe_browsing::VerifyAppsEnabledResult;
-using safe_browsing::VerifyAppsResponseCallback;
 
 namespace device_signals {
 
+namespace {
+
+void LogHarmfulAppsResult(HasHarmfulAppsResultStatus result, int num_of_apps) {
+  static constexpr char kHarmfulAppsResultHistogram[] =
+      "Enterprise.DeviceSignals.HarmfulApps.%s";
+  // TODO(crbug.com/449183636): Ideally we should log the reason of failure as
+  // well.
+  base::UmaHistogramEnumeration(
+      base::StringPrintf(kHarmfulAppsResultHistogram, "Result"), result);
+  if (result == HasHarmfulAppsResultStatus::SUCCESS) {
+    base::UmaHistogramCounts100(
+        base::StringPrintf(kHarmfulAppsResultHistogram, "Count"), num_of_apps);
+  }
+}
+
+}  // namespace
+
 AndroidOsSignalsCollector::AndroidOsSignalsCollector(
     policy::CloudPolicyManager* device_cloud_policy_manager)
     : BaseSignalsCollector({
@@ -46,15 +65,13 @@
   auto signal_response = std::make_unique<OsSignalsResponse>();
 
   safe_browsing::SafeBrowsingApiHandlerBridge::GetInstance()
-      .StartIsVerifyAppsEnabled(base::BindOnce(
-          &AndroidOsSignalsCollector::OnIsVerifyAppsEnabled,
-          weak_factory_.GetWeakPtr(), permission, request, std::ref(response),
-          std::move(signal_response), std::move(done_closure)));
+      .StartIsVerifyAppsEnabled(
+          base::BindOnce(&AndroidOsSignalsCollector::OnIsVerifyAppsEnabled,
+                         weak_factory_.GetWeakPtr(), std::ref(response),
+                         std::move(signal_response), std::move(done_closure)));
 }
 
 void AndroidOsSignalsCollector::OnIsVerifyAppsEnabled(
-    UserPermission permission,
-    const SignalsAggregationRequest& request,
     SignalsAggregationResponse& response,
     std::unique_ptr<OsSignalsResponse> os_signals_response,
     base::OnceClosure done_closure,
@@ -63,6 +80,22 @@
       (result == VerifyAppsEnabledResult::SUCCESS_ENABLED ||
        result == VerifyAppsEnabledResult::SUCCESS_ALREADY_ENABLED);
 
+  safe_browsing::SafeBrowsingApiHandlerBridge::GetInstance()
+      .StartHasPotentiallyHarmfulApps(base::BindOnce(
+          &AndroidOsSignalsCollector::OnHasPotentiallyHarmfulApps,
+          weak_factory_.GetWeakPtr(), std::ref(response),
+          std::move(os_signals_response), std::move(done_closure)));
+}
+
+void AndroidOsSignalsCollector::OnHasPotentiallyHarmfulApps(
+    SignalsAggregationResponse& response,
+    std::unique_ptr<OsSignalsResponse> os_signals_response,
+    base::OnceClosure done_closure,
+    HasHarmfulAppsResultStatus result,
+    int num_of_apps) {
+  os_signals_response->has_potentially_harmful_apps =
+      result == HasHarmfulAppsResultStatus::SUCCESS && num_of_apps != 0;
+  LogHarmfulAppsResult(result, num_of_apps);
   response.os_signals_response = std::move(*os_signals_response);
 
   std::move(done_closure).Run();
diff --git a/components/device_signals/core/browser/android/android_os_signals_collector.h b/components/device_signals/core/browser/android/android_os_signals_collector.h
index 2be79fb..c131642 100644
--- a/components/device_signals/core/browser/android/android_os_signals_collector.h
+++ b/components/device_signals/core/browser/android/android_os_signals_collector.h
@@ -15,9 +15,7 @@
 namespace safe_browsing {
 
 enum class VerifyAppsEnabledResult;
-
-using VerifyAppsResponseCallback =
-    base::OnceCallback<void(VerifyAppsEnabledResult)>;
+enum class HasHarmfulAppsResultStatus;
 
 }  // namespace safe_browsing
 
@@ -43,13 +41,18 @@
                     base::OnceClosure done_closure);
 
   void OnIsVerifyAppsEnabled(
-      UserPermission permission,
-      const SignalsAggregationRequest& request,
       SignalsAggregationResponse& response,
       std::unique_ptr<OsSignalsResponse> os_signals_response,
       base::OnceClosure done_closure,
       safe_browsing::VerifyAppsEnabledResult result);
 
+  void OnHasPotentiallyHarmfulApps(
+      SignalsAggregationResponse& response,
+      std::unique_ptr<OsSignalsResponse> os_signals_response,
+      base::OnceClosure done_closure,
+      safe_browsing::HasHarmfulAppsResultStatus result,
+      int num_of_apps);
+
   const raw_ptr<policy::CloudPolicyManager> device_cloud_policy_manager_;
   base::WeakPtrFactory<AndroidOsSignalsCollector> weak_factory_{this};
 };
diff --git a/components/device_signals/core/browser/android/android_os_signals_collector_unittest.cc b/components/device_signals/core/browser/android/android_os_signals_collector_unittest.cc
index f9b0317..a5904ee 100644
--- a/components/device_signals/core/browser/android/android_os_signals_collector_unittest.cc
+++ b/components/device_signals/core/browser/android/android_os_signals_collector_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/values.h"
 #include "components/device_signals/core/browser/signals_types.h"
 #include "components/device_signals/core/browser/user_permission_service.h"
@@ -29,9 +30,14 @@
 
 namespace {
 
+using safe_browsing::HasHarmfulAppsResultStatus;
 using safe_browsing::VerifyAppsEnabledResult;
 
 constexpr char kFakeBrowserEnrollmentDomain[] = "fake.domain.google.com";
+constexpr char kHarmfulAppsResultHistogramName[] =
+    "Enterprise.DeviceSignals.HarmfulApps.Result";
+constexpr char kHarmfulAppsCountHistogramName[] =
+    "Enterprise.DeviceSignals.HarmfulApps.Count";
 
 }  // namespace
 
@@ -53,6 +59,7 @@
         mock_browser_cloud_policy_manager_.get());
 
     SetVerifyAppsResult(VerifyAppsEnabledResult::SUCCESS_NOT_ENABLED);
+    SetHarmfulAppsResult(HasHarmfulAppsResultStatus::SUCCESS, 2);
   }
 
   void TearDown() override { mock_browser_cloud_policy_store_ = nullptr; }
@@ -71,6 +78,22 @@
     // `can_collect_pii` will be used when we add the remaining signals, so
     // leaving it unused here for now.
     EXPECT_EQ(response.verified_apps_enabled, expected_verify_app_result_);
+    EXPECT_EQ(response.has_potentially_harmful_apps,
+              GetExpectedHarmfulAppsSignal());
+
+    CheckUmaHistograms();
+  }
+
+  void CheckUmaHistograms() {
+    histogram_tester_.ExpectUniqueSample(kHarmfulAppsResultHistogramName,
+                                         expected_harmful_app_result_, 1);
+
+    if (expected_harmful_app_result_ == HasHarmfulAppsResultStatus::SUCCESS) {
+      histogram_tester_.ExpectUniqueSample(kHarmfulAppsCountHistogramName,
+                                           expected_harmful_app_count_, 1);
+    } else {
+      histogram_tester_.ExpectTotalCount(kHarmfulAppsCountHistogramName, 0);
+    }
   }
 
   void SetVerifyAppsResult(VerifyAppsEnabledResult result) {
@@ -81,12 +104,29 @@
          result == VerifyAppsEnabledResult::SUCCESS_ALREADY_ENABLED);
   }
 
+  void SetHarmfulAppsResult(HasHarmfulAppsResultStatus result,
+                            int num_of_apps) {
+    safe_browsing::SafeBrowsingApiHandlerBridge::GetInstance()
+        .SetHarmfulAppsResultForTesting(result, num_of_apps);
+    expected_harmful_app_result_ = result;
+    expected_harmful_app_count_ = num_of_apps;
+  }
+
+  bool GetExpectedHarmfulAppsSignal() {
+    return (expected_harmful_app_result_ ==
+                HasHarmfulAppsResultStatus::SUCCESS &&
+            expected_harmful_app_count_ != 0);
+  }
+
   content::BrowserTaskEnvironment task_environment_;
   std::unique_ptr<policy::MockCloudPolicyManager>
       mock_browser_cloud_policy_manager_;
   raw_ptr<policy::MockCloudPolicyStore> mock_browser_cloud_policy_store_;
   std::unique_ptr<AndroidOsSignalsCollector> signal_collector_;
   bool expected_verify_app_result_;
+  HasHarmfulAppsResultStatus expected_harmful_app_result_;
+  int expected_harmful_app_count_;
+  base::HistogramTester histogram_tester_;
 };
 
 // Test that runs a sanity check on the set of signals supported by this
@@ -107,6 +147,8 @@
   SetFakeBrowserPolicyData();
   // Test when verify apps is enabled.
   SetVerifyAppsResult(VerifyAppsEnabledResult::SUCCESS_ENABLED);
+  // Test when harmful apps detection fails.
+  SetHarmfulAppsResult(HasHarmfulAppsResultStatus::FAILED, 0);
 
   SignalName signal_name = SignalName::kOsSignals;
   SignalsAggregationRequest empty_request;
diff --git a/components/dom_distiller/core/css/distilledpage_new.css b/components/dom_distiller/core/css/distilledpage_new.css
index cd0b21ca..305010b 100644
--- a/components/dom_distiller/core/css/distilledpage_new.css
+++ b/components/dom_distiller/core/css/distilledpage_new.css
@@ -108,10 +108,16 @@
   width: 100%;
 }
 
+@media (prefers-color-scheme: dark) {
+  img.distilled-inline-img {
+    filter: invert(1) hue-rotate(180deg);
+  }
+}
+
 img, figcaption, picture:first-child + p:last-child {
-    font-size: 0.75rem; /* 12px */
-    line-height: 1.125rem; /* 18px */
-    font-style: italic;
+  font-size: 0.75rem; /* 12px */
+  line-height: 1.125rem; /* 18px */
+  font-style: italic;
 }
 
 /* list specific styling */
diff --git a/components/dom_distiller/core/javascript/dom_distiller_viewer.js b/components/dom_distiller/core/javascript/dom_distiller_viewer.js
index 9619d5be..e67c637 100644
--- a/components/dom_distiller/core/javascript/dom_distiller_viewer.js
+++ b/components/dom_distiller/core/javascript/dom_distiller_viewer.js
@@ -25,18 +25,27 @@
   return document.getElementById(id);
 }
 
+/**
+ * A helper function that calls the post-processing functions on a given
+ * element.
+ * @param {HTMLElement} element The container element of the article.
+ */
+function postProcessElement(element) {
+  // Readability will leave iframes around, but they need the proper structure
+  // and classes to be styled correctly.
+  addClassesToYoutubeIFrames(element);
+  // DomDistiller will leave placeholders, which need to be replaced with
+  // actual iframes.
+  fillYouTubePlaceholders(element);
+  sanitizeLinks(element);
+  ImageClassifier.processImagesIn(element);
+}
+
 function addToPage(html) {
   const div = document.createElement('div');
   div.innerHTML = html;
   $('content').appendChild(div);
-  // Readability will leave iframes around, but they need the proper structure
-  // and classes to be styled correctly.
-  addClassesToYoutubeIFrames(div);
-  // DomDistiller will leave placeholders, which need to be replaced with
-  // actual iframes.
-  fillYouTubePlaceholders(div);
-  sanitizeLinks(div);
-  ImageClassifier.processImagesIn(div);
+  postProcessElement(div);
 }
 
 /**
diff --git a/components/enterprise/browser/reporting/profile_report_generator.cc b/components/enterprise/browser/reporting/profile_report_generator.cc
index 6ff8eba..7adf5f0 100644
--- a/components/enterprise/browser/reporting/profile_report_generator.cc
+++ b/components/enterprise/browser/reporting/profile_report_generator.cc
@@ -55,13 +55,23 @@
 
   report_ = std::make_unique<em::ChromeUserProfileInfo>();
 
+#if !BUILDFLAG(IS_CHROMEOS)
+  delegate_->GetAffiliationInfo(report_.get());
+#endif
+
   switch (report_type) {
-    // TODO(crbug.com/330336666): Rename report type `kFull` to `kBrowser`.
+    // TODO(crbug.com/441536805): Rename report type `kFull` to `kBrowser`.
     case ReportType::kFull:
       report_->set_id(path.AsUTF8Unsafe());
       break;
     case ReportType::kProfileReport:
-      report_->set_id(ObfuscateFilePath(path.AsUTF8Unsafe()));
+      if (report_->has_affiliation() &&
+          report_->affiliation().has_is_affiliated() &&
+          report_->affiliation().is_affiliated()) {
+        report_->set_id(path.AsUTF8Unsafe());
+      } else {
+        report_->set_id(ObfuscateFilePath(path.AsUTF8Unsafe()));
+      }
       break;
     case ReportType::kBrowserVersion:
       NOTREACHED();
@@ -72,10 +82,6 @@
   delegate_->GetSigninUserInfo(report_.get());
   delegate_->GetProfileName(report_.get());
 
-#if !BUILDFLAG(IS_CHROMEOS)
-  delegate_->GetAffiliationInfo(report_.get());
-#endif
-
   if (extensions_enabled_ &&
       (!extensions_enabled_callback_ || extensions_enabled_callback_.Run())) {
     delegate_->GetExtensionInfo(report_.get());
diff --git a/components/enterprise/connectors/core/analysis_service_settings_base.cc b/components/enterprise/connectors/core/analysis_service_settings_base.cc
index 87f8ebb..8aab3aaa 100644
--- a/components/enterprise/connectors/core/analysis_service_settings_base.cc
+++ b/components/enterprise/connectors/core/analysis_service_settings_base.cc
@@ -11,6 +11,25 @@
 
 namespace enterprise_connectors {
 
+AnalysisServiceSettingsBase::AnalysisServiceSettingsBase(
+    const base::Value& settings_value,
+    const ServiceProviderConfig& service_provider_config) {
+  if (!settings_value.is_dict()) {
+    return;
+  }
+
+  const auto& settings_dict = settings_value.GetDict();
+
+  if (!TryParseServiceProviderData(settings_dict, service_provider_config)) {
+    return;
+  }
+
+  ParseBlockSettings(settings_dict);
+  ParseMinimumDataSize(settings_dict);
+  ParseCustomMessages(settings_dict);
+  ParseJustificationTags(settings_dict);
+}
+
 bool AnalysisServiceSettingsBase::TryParseServiceProviderData(
     const base::Value::Dict& settings_dict,
     const ServiceProviderConfig& service_provider_config) {
@@ -194,7 +213,134 @@
   }
 }
 
-AnalysisServiceSettingsBase::AnalysisServiceSettingsBase() = default;
+bool AnalysisServiceSettingsBase::IsValid() const {
+  // The settings are invalid if no provider was given.
+  if (!analysis_config_) {
+    return false;
+  }
+
+  // The settings are invalid if no enabled pattern(s) exist since that would
+  // imply no URL can ever have an analysis.
+  return !enabled_patterns_settings_.empty();
+}
+
+std::map<std::string, TagSettings> AnalysisServiceSettingsBase::GetTags(
+    const std::set<base::MatcherStringPattern::ID>& matches) const {
+  std::set<std::string> enable_tags;
+  std::set<std::string> disable_tags;
+  for (const base::MatcherStringPattern::ID match : matches) {
+    // Enabled patterns need to be checked first, otherwise they always match
+    // the first disabled pattern.
+    bool enable = true;
+    auto maybe_pattern_setting =
+        GetPatternSettings(enabled_patterns_settings_, match);
+    if (!maybe_pattern_setting.has_value()) {
+      maybe_pattern_setting =
+          GetPatternSettings(disabled_patterns_settings_, match);
+      enable = false;
+    }
+
+    DCHECK(maybe_pattern_setting.has_value());
+    auto tags = std::move(maybe_pattern_setting.value().tags);
+    if (enable) {
+      enable_tags.insert(tags.begin(), tags.end());
+    } else {
+      disable_tags.insert(tags.begin(), tags.end());
+    }
+  }
+
+  for (const std::string& tag_to_disable : disable_tags) {
+    enable_tags.erase(tag_to_disable);
+  }
+
+  std::map<std::string, TagSettings> output;
+  for (const std::string& tag : enable_tags) {
+    if (tags_.count(tag)) {
+      output[tag] = tags_.at(tag);
+    } else {
+      output[tag] = TagSettings();
+    }
+  }
+
+  return output;
+}
+
+// static
+std::optional<AnalysisServiceSettingsBase::URLPatternSettings>
+AnalysisServiceSettingsBase::GetPatternSettings(
+    const PatternSettings& patterns,
+    base::MatcherStringPattern::ID match) {
+  // If the pattern exists directly in the map, return its settings.
+  if (patterns.count(match) == 1) {
+    return patterns.at(match);
+  }
+
+  // If the pattern doesn't exist in the map, it might mean that it wasn't the
+  // only pattern to correspond to its settings and that the ID added to
+  // the map was the one of the last pattern corresponding to those settings.
+  // This means the next match ID greater than |match| has the correct
+  // settings if it exists.
+  auto next = patterns.upper_bound(match);
+  if (next != patterns.end()) {
+    return next->second;
+  }
+
+  return std::nullopt;
+}
+
+bool AnalysisServiceSettingsBase::ShouldBlockUntilVerdict() const {
+  if (!IsValid()) {
+    return false;
+  }
+
+  return block_until_verdict_ == BlockUntilVerdict::kBlock;
+}
+
+bool AnalysisServiceSettingsBase::ShouldBlockByDefault() const {
+  if (!IsValid()) {
+    return false;
+  }
+
+  return default_action_ == DefaultAction::kBlock;
+}
+
+std::optional<std::u16string> AnalysisServiceSettingsBase::GetCustomMessage(
+    const std::string& tag) {
+  const auto& element = tags_.find(tag);
+
+  if (!IsValid() || element == tags_.end() ||
+      element->second.custom_message.message.empty()) {
+    return std::nullopt;
+  }
+
+  return element->second.custom_message.message;
+}
+
+std::optional<GURL> AnalysisServiceSettingsBase::GetLearnMoreUrl(
+    const std::string& tag) {
+  const auto& element = tags_.find(tag);
+
+  if (!IsValid() || element == tags_.end() ||
+      element->second.custom_message.learn_more_url.is_empty()) {
+    return std::nullopt;
+  }
+
+  return element->second.custom_message.learn_more_url;
+}
+
+bool AnalysisServiceSettingsBase::GetBypassJustificationRequired(
+    const std::string& tag) {
+  return tags_.find(tag) != tags_.end() && tags_.at(tag).requires_justification;
+}
+
+bool AnalysisServiceSettingsBase::is_cloud_analysis() const {
+  return analysis_config_ && analysis_config_->url != nullptr;
+}
+
+bool AnalysisServiceSettingsBase::is_local_analysis() const {
+  return analysis_config_ && analysis_config_->local_path != nullptr;
+}
+
 AnalysisServiceSettingsBase::AnalysisServiceSettingsBase(
     AnalysisServiceSettingsBase&&) = default;
 AnalysisServiceSettingsBase& AnalysisServiceSettingsBase::operator=(
diff --git a/components/enterprise/connectors/core/analysis_service_settings_base.h b/components/enterprise/connectors/core/analysis_service_settings_base.h
index bf373ab..d28913a 100644
--- a/components/enterprise/connectors/core/analysis_service_settings_base.h
+++ b/components/enterprise/connectors/core/analysis_service_settings_base.h
@@ -30,6 +30,26 @@
 
   virtual ~AnalysisServiceSettingsBase();
 
+  // Get the block_until_verdict setting if the settings are valid.
+  bool ShouldBlockUntilVerdict() const;
+
+  // Get the default_action setting if the settings are valid.
+  bool ShouldBlockByDefault() const;
+
+  // Get the custom message/learn more URL. Returns std::nullopt if the
+  // settings are invalid or if the message/URL are empty.
+  std::optional<std::u16string> GetCustomMessage(const std::string& tag);
+  std::optional<GURL> GetLearnMoreUrl(const std::string& tag);
+  bool GetBypassJustificationRequired(const std::string& tag);
+
+  std::string service_provider_name() const { return service_provider_name_; }
+
+  // Helpers for convenient check of the underlying variant.
+  bool is_cloud_analysis() const;
+  bool is_local_analysis() const;
+
+  const AnalysisConfig* GetAnalysisConfig() const { return analysis_config_; }
+
  protected:
   // The setting to apply when a specific URL pattern is matched.
   struct URLPatternSettings {
@@ -48,7 +68,9 @@
   using PatternSettings =
       std::map<base::MatcherStringPattern::ID, URLPatternSettings>;
 
-  AnalysisServiceSettingsBase();
+  explicit AnalysisServiceSettingsBase(
+      const base::Value& settings_value,
+      const ServiceProviderConfig& service_provider_config);
 
   // Helper methods for parsing the raw policy settings input
   // Service provider data must be provided and valid
@@ -68,6 +90,15 @@
                              bool enabled,
                              base::MatcherStringPattern::ID* id);
 
+  // Returns true if the settings were initialized correctly. If this returns
+  // false, then GetAnalysisSettings will always return std::nullopt.
+  bool IsValid() const;
+
+  // Return tags found in |enabled_patterns_settings| corresponding to the
+  // matches while excluding the ones in |disable_patterns_settings|.
+  std::map<std::string, TagSettings> GetTags(
+      const std::set<base::MatcherStringPattern::ID>& matches) const;
+
   // The service provider matching the name given in a Connector policy. nullptr
   // implies that a corresponding service provider doesn't exist and that these
   // settings are not valid.
@@ -106,6 +137,11 @@
  private:
   static constexpr size_t kDefaultMinimumDataSize = 100;
 
+  // Accessors for the pattern setting maps.
+  static std::optional<URLPatternSettings> GetPatternSettings(
+      const PatternSettings& patterns,
+      base::MatcherStringPattern::ID match);
+
   // Parses the "source_destination_list" from the analysis settings. This is
   // intended to be called only during the construction of the base class.
   // This field is only supported on the ChromeOS platform, so this method does
diff --git a/components/enterprise/connectors/core/reporting_utils_unittest.cc b/components/enterprise/connectors/core/reporting_utils_unittest.cc
index 67208ca9..7b06b87 100644
--- a/components/enterprise/connectors/core/reporting_utils_unittest.cc
+++ b/components/enterprise/connectors/core/reporting_utils_unittest.cc
@@ -401,7 +401,7 @@
 #if BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
 TEST(ReportingUtilsTest, GetDataControlsSensitiveDataEvent) {
   data_controls::Verdict::TriggeredRules triggered_rules = {
-      {0, {"1", "rule_1_name"}}};
+      {{0, true}, {"1", "rule_1_name"}}};
 
   auto event = GetDataControlsSensitiveDataEvent(
       /*url=*/GURL("https://google.com/"), /*tab_url=*/GURL("about:blank"),
diff --git a/components/enterprise/data_controls/core/browser/rules_service_base.cc b/components/enterprise/data_controls/core/browser/rules_service_base.cc
index 9875c3c..591d7cd6 100644
--- a/components/enterprise/data_controls/core/browser/rules_service_base.cc
+++ b/components/enterprise/data_controls/core/browser/rules_service_base.cc
@@ -5,6 +5,7 @@
 #include "components/enterprise/data_controls/core/browser/rules_service_base.h"
 
 #include "components/enterprise/data_controls/core/browser/prefs.h"
+#include "components/policy/core/common/policy_types.h"
 #include "components/prefs/pref_service.h"
 
 namespace data_controls {
@@ -70,7 +71,10 @@
       max_level = level;
     }
     if (level != Rule::Level::kNotSet) {
-      triggered_rules[i] = {
+      triggered_rules[Verdict::TriggeredRuleKey{
+          .index = i,
+          .machine_scope = MachineScopePolicy(),
+      }] = {
           .rule_id = rule.rule_id(),
           .rule_name = rule.name(),
       };
@@ -91,6 +95,11 @@
   }
 }
 
+bool RulesServiceBase::MachineScopePolicy() const {
+  return pref_registrar_.prefs()->GetInteger(kDataControlsRulesScopePref) ==
+         policy::POLICY_SCOPE_MACHINE;
+}
+
 void RulesServiceBase::OnDataControlsRulesUpdate() {
   DCHECK(pref_registrar_.prefs());
   rules_.clear();
diff --git a/components/enterprise/data_controls/core/browser/rules_service_base.h b/components/enterprise/data_controls/core/browser/rules_service_base.h
index 3dca7f83..4708cf9 100644
--- a/components/enterprise/data_controls/core/browser/rules_service_base.h
+++ b/components/enterprise/data_controls/core/browser/rules_service_base.h
@@ -49,6 +49,10 @@
   // or not.
   virtual bool incognito_profile() const = 0;
 
+  // Returns if the "DataControlsRules" policy for the current service is set at
+  // the machine scope or not.
+  bool MachineScopePolicy() const;
+
   // Parse the "DataControlsRules" policy if the corresponding experiment is
   // enabled, and populate `rules_`.
   void OnDataControlsRulesUpdate();
diff --git a/components/enterprise/data_controls/core/browser/rules_service_base_unittest.cc b/components/enterprise/data_controls/core/browser/rules_service_base_unittest.cc
index 85f12ab..a598078 100644
--- a/components/enterprise/data_controls/core/browser/rules_service_base_unittest.cc
+++ b/components/enterprise/data_controls/core/browser/rules_service_base_unittest.cc
@@ -19,22 +19,30 @@
   return GURL("https://google.com");
 }
 
-void ExpectBlockVerdict(Verdict verdict) {
+void ExpectBlockVerdict(Verdict verdict, bool expect_machine_source = true) {
   ASSERT_EQ(verdict.level(), Rule::Level::kBlock);
   EXPECT_EQ(verdict.triggered_rules().size(), 1u);
-  EXPECT_TRUE(verdict.triggered_rules().count(kFirstRuleIndex));
-  EXPECT_EQ(verdict.triggered_rules().at(kFirstRuleIndex).rule_name, "block");
-  EXPECT_EQ(verdict.triggered_rules().at(kFirstRuleIndex).rule_id,
-            kFirstRuleID);
+
+  auto index = Verdict::TriggeredRuleKey{
+      .index = kFirstRuleIndex,
+      .machine_scope = expect_machine_source,
+  };
+  EXPECT_TRUE(verdict.triggered_rules().count(index));
+  EXPECT_EQ(verdict.triggered_rules().at(index).rule_name, "block");
+  EXPECT_EQ(verdict.triggered_rules().at(index).rule_id, kFirstRuleID);
 }
 
-void ExpectWarnVerdict(Verdict verdict) {
+void ExpectWarnVerdict(Verdict verdict, bool expect_machine_source = true) {
   ASSERT_EQ(verdict.level(), Rule::Level::kWarn);
   EXPECT_EQ(verdict.triggered_rules().size(), 1u);
-  EXPECT_TRUE(verdict.triggered_rules().count(kFirstRuleIndex));
-  EXPECT_EQ(verdict.triggered_rules().at(kFirstRuleIndex).rule_name, "warn");
-  EXPECT_EQ(verdict.triggered_rules().at(kFirstRuleIndex).rule_id,
-            kFirstRuleID);
+
+  auto index = Verdict::TriggeredRuleKey{
+      .index = kFirstRuleIndex,
+      .machine_scope = expect_machine_source,
+  };
+  EXPECT_TRUE(verdict.triggered_rules().count(index));
+  EXPECT_EQ(verdict.triggered_rules().at(index).rule_name, "warn");
+  EXPECT_EQ(verdict.triggered_rules().at(index).rule_id, kFirstRuleID);
 }
 
 void ExpectAllowVerdict(Verdict verdict) {
@@ -85,6 +93,27 @@
       incognito_service_->GetCopyToOSClipboardVerdict(google_url()));
 }
 
+TEST_F(RulesServiceBaseTest, PolicyScope) {
+  constexpr char kPolicy[] = R"({
+                    "name": "block",
+                    "rule_id": "1234",
+                    "sources": {
+                      "urls": ["google.com"]
+                    },
+                    "restrictions": [
+                      {"class": "CLIPBOARD", "level": "BLOCK"}
+                    ]
+                  })";
+
+  SetDataControls(&prefs_, {kPolicy}, true);
+  ExpectBlockVerdict(service_->GetCopyRestrictedBySourceVerdict(google_url()),
+                     /*expect_machine_scope=*/true);
+
+  SetDataControls(&prefs_, {kPolicy}, false);
+  ExpectBlockVerdict(service_->GetCopyRestrictedBySourceVerdict(google_url()),
+                     /*expect_machine_scope=*/false);
+}
+
 TEST_F(RulesServiceBaseTest, BlockCopyForSourceUrl) {
   SetDataControls(&prefs_, {R"({
                     "name": "block",
diff --git a/components/enterprise/data_controls/core/browser/verdict.cc b/components/enterprise/data_controls/core/browser/verdict.cc
index faf51046..3e71c1a 100644
--- a/components/enterprise/data_controls/core/browser/verdict.cc
+++ b/components/enterprise/data_controls/core/browser/verdict.cc
@@ -8,6 +8,13 @@
 
 namespace data_controls {
 
+bool Verdict::TriggeredRuleKey::operator<(const TriggeredRuleKey& other) const {
+  if (machine_scope == other.machine_scope) {
+    return index < other.index;
+  }
+  return machine_scope;
+}
+
 // static
 Verdict Verdict::NotSet() {
   return Verdict(Rule::Level::kNotSet, {});
@@ -40,6 +47,15 @@
     destination_verdict.level_ = source_verdict.level();
   }
 
+  for (const auto& rule : source_verdict.triggered_rules()) {
+    if (rule.first.machine_scope) {
+      // Since `rule`'s key contains a boolean indicating its scope, it's safe
+      // to merge when it's a machine scope as any conflict it would have with
+      // `destination_verdict` will be for the same rule.
+      destination_verdict.triggered_rules_.insert(rule);
+    }
+  }
+
   return destination_verdict;
 }
 
diff --git a/components/enterprise/data_controls/core/browser/verdict.h b/components/enterprise/data_controls/core/browser/verdict.h
index f40069f..3de7894 100644
--- a/components/enterprise/data_controls/core/browser/verdict.h
+++ b/components/enterprise/data_controls/core/browser/verdict.h
@@ -17,16 +17,23 @@
 class Verdict {
  public:
   // The key is the rule's index in the "DataControlsRules" policy list
-  // representation that exists in `RulesService`.
+  // representation that exists in `RulesService` combined with a boolean
+  // representing if it comes from a machine-scope policy or not.
   // Since policy updates can change the rules list and invalidate indexes of
   // previously triggered rules, this index key should only be used
   // synchronously to merge rules and not in async cases (for example after a
   // warning dialog has been shown).
+  struct TriggeredRuleKey {
+    size_t index;
+    bool machine_scope;
+
+    bool operator<(const TriggeredRuleKey& other) const;
+  };
   struct TriggeredRule {
     std::string rule_id;
     std::string rule_name;
   };
-  using TriggeredRules = base::flat_map<size_t, TriggeredRule>;
+  using TriggeredRules = base::flat_map<TriggeredRuleKey, TriggeredRule>;
 
   static Verdict NotSet();
   static Verdict Report(TriggeredRules triggered_rules);
@@ -37,7 +44,8 @@
   // Creates a combination of two `Verdict`s when both a source and destination
   // `Verdict` are obtained in a single paste. The merge `Verdict` has the
   // highest precedence between the two original verdicts, but only the
-  // triggered rules of the destination one for reporting.
+  // triggered rules of the destination and machine-level rules of the source
+  // verdict are reported.
   static Verdict MergePasteVerdicts(Verdict source_verdict,
                                     Verdict destination_verdict);
 
diff --git a/components/enterprise/data_controls/core/browser/verdict_unittest.cc b/components/enterprise/data_controls/core/browser/verdict_unittest.cc
index f332376..3e3b4e8 100644
--- a/components/enterprise/data_controls/core/browser/verdict_unittest.cc
+++ b/components/enterprise/data_controls/core/browser/verdict_unittest.cc
@@ -13,9 +13,18 @@
 
 namespace {
 
-constexpr size_t kReportRuleIndex = 0;
-constexpr size_t kWarnRuleIndex = 1;
-constexpr size_t kBlockRuleIndex = 2;
+constexpr Verdict::TriggeredRuleKey kReportRuleKey = {
+    .index = 0,
+    .machine_scope = true,
+};
+constexpr Verdict::TriggeredRuleKey kWarnRuleKey = {
+    .index = 1,
+    .machine_scope = true,
+};
+constexpr Verdict::TriggeredRuleKey kBlockRuleKey = {
+    .index = 2,
+    .machine_scope = true,
+};
 
 constexpr char kReportRuleID[] = "report_rule_id";
 constexpr char kWarnRuleID[] = "warn_rule_id";
@@ -31,18 +40,35 @@
 }
 Verdict Report() {
   return Verdict::Report({
-      {kReportRuleIndex,
-       {.rule_id = kReportRuleID, .rule_name = kReportRuleName}},
+      {
+          kReportRuleKey,
+          {
+              .rule_id = kReportRuleID,
+              .rule_name = kReportRuleName,
+          },
+      },
   });
 }
 Verdict Warn() {
   return Verdict::Warn({
-      {kWarnRuleIndex, {.rule_id = kWarnRuleID, .rule_name = kWarnRuleName}},
+      {
+          kWarnRuleKey,
+          {
+              .rule_id = kWarnRuleID,
+              .rule_name = kWarnRuleName,
+          },
+      },
   });
 }
 Verdict Block() {
   return Verdict::Block({
-      {kBlockRuleIndex, {.rule_id = kBlockRuleID, .rule_name = kBlockRuleName}},
+      {
+          kBlockRuleKey,
+          {
+              .rule_id = kBlockRuleID,
+              .rule_name = kBlockRuleName,
+          },
+      },
   });
 }
 Verdict Allow() {
@@ -157,23 +183,22 @@
 
   auto report = Report();
   EXPECT_EQ(report.triggered_rules().size(), 1u);
-  EXPECT_TRUE(report.triggered_rules().count(kReportRuleIndex));
-  EXPECT_EQ(report.triggered_rules().at(kReportRuleIndex).rule_id,
-            kReportRuleID);
-  EXPECT_EQ(report.triggered_rules().at(kReportRuleIndex).rule_name,
+  EXPECT_TRUE(report.triggered_rules().count(kReportRuleKey));
+  EXPECT_EQ(report.triggered_rules().at(kReportRuleKey).rule_id, kReportRuleID);
+  EXPECT_EQ(report.triggered_rules().at(kReportRuleKey).rule_name,
             kReportRuleName);
 
   auto warn = Warn();
   EXPECT_EQ(warn.triggered_rules().size(), 1u);
-  EXPECT_TRUE(warn.triggered_rules().count(kWarnRuleIndex));
-  EXPECT_EQ(warn.triggered_rules().at(kWarnRuleIndex).rule_id, kWarnRuleID);
-  EXPECT_EQ(warn.triggered_rules().at(kWarnRuleIndex).rule_name, kWarnRuleName);
+  EXPECT_TRUE(warn.triggered_rules().count(kWarnRuleKey));
+  EXPECT_EQ(warn.triggered_rules().at(kWarnRuleKey).rule_id, kWarnRuleID);
+  EXPECT_EQ(warn.triggered_rules().at(kWarnRuleKey).rule_name, kWarnRuleName);
 
   auto block = Block();
   EXPECT_EQ(block.triggered_rules().size(), 1u);
-  EXPECT_TRUE(block.triggered_rules().count(kBlockRuleIndex));
-  EXPECT_EQ(block.triggered_rules().at(kBlockRuleIndex).rule_id, kBlockRuleID);
-  EXPECT_EQ(block.triggered_rules().at(kBlockRuleIndex).rule_name,
+  EXPECT_TRUE(block.triggered_rules().count(kBlockRuleKey));
+  EXPECT_EQ(block.triggered_rules().at(kBlockRuleKey).rule_id, kBlockRuleID);
+  EXPECT_EQ(block.triggered_rules().at(kBlockRuleKey).rule_name,
             kBlockRuleName);
 }
 
@@ -182,22 +207,66 @@
   // internally duplicate the rule in two.
   auto merged_warnings = Verdict::MergePasteVerdicts(Warn(), Warn());
   EXPECT_EQ(merged_warnings.triggered_rules().size(), 1u);
-  EXPECT_TRUE(merged_warnings.triggered_rules().count(kWarnRuleIndex));
-  EXPECT_EQ(merged_warnings.triggered_rules().at(kWarnRuleIndex).rule_id,
+  EXPECT_TRUE(merged_warnings.triggered_rules().count(kWarnRuleKey));
+  EXPECT_EQ(merged_warnings.triggered_rules().at(kWarnRuleKey).rule_id,
             kWarnRuleID);
-  EXPECT_EQ(merged_warnings.triggered_rules().at(kWarnRuleIndex).rule_name,
+  EXPECT_EQ(merged_warnings.triggered_rules().at(kWarnRuleKey).rule_name,
             kWarnRuleName);
 
-  // Merging three verdicts with different rules should rules in a verdict with
-  // only the destination triggered rules.
+  // Merging three verdicts with different rules should result in a verdict with
+  // all triggered rules as they are all machine-scoped.
   auto all_merged = Verdict::MergePasteVerdicts(
       Warn(), Verdict::MergePasteVerdicts(Report(), Block()));
-  EXPECT_EQ(all_merged.triggered_rules().size(), 1u);
-  EXPECT_TRUE(all_merged.triggered_rules().count(kBlockRuleIndex));
-  EXPECT_EQ(all_merged.triggered_rules().at(kBlockRuleIndex).rule_id,
+  EXPECT_EQ(all_merged.triggered_rules().size(), 3u);
+
+  EXPECT_TRUE(all_merged.triggered_rules().count(kReportRuleKey));
+  EXPECT_TRUE(all_merged.triggered_rules().count(kWarnRuleKey));
+  EXPECT_TRUE(all_merged.triggered_rules().count(kBlockRuleKey));
+
+  EXPECT_EQ(all_merged.triggered_rules().at(kReportRuleKey).rule_id,
+            kReportRuleID);
+  EXPECT_EQ(all_merged.triggered_rules().at(kReportRuleKey).rule_name,
+            kReportRuleName);
+
+  EXPECT_EQ(all_merged.triggered_rules().at(kWarnRuleKey).rule_id, kWarnRuleID);
+  EXPECT_EQ(all_merged.triggered_rules().at(kWarnRuleKey).rule_name,
+            kWarnRuleName);
+
+  EXPECT_EQ(all_merged.triggered_rules().at(kBlockRuleKey).rule_id,
             kBlockRuleID);
-  EXPECT_EQ(all_merged.triggered_rules().at(kBlockRuleIndex).rule_name,
+  EXPECT_EQ(all_merged.triggered_rules().at(kBlockRuleKey).rule_name,
             kBlockRuleName);
+
+  // Merging two verdicts with profile-scoped rules should result in a verdict
+  // with only the destination triggered rules, even if their index is the same.
+  constexpr auto kIdenticalKey =
+      Verdict::TriggeredRuleKey{.index = 0, .machine_scope = false};
+
+  auto profile_verdicts_merged =
+      Verdict::MergePasteVerdicts(Verdict::Block({{
+                                      kIdenticalKey,
+                                      {
+                                          .rule_id = "rule_id_1",
+                                          .rule_name = "rule_name_1",
+                                      },
+                                  }}),
+                                  Verdict::Warn({{
+                                      kIdenticalKey,
+                                      {
+                                          .rule_id = "rule_id_2",
+                                          .rule_name = "rule_name_2",
+                                      },
+                                  }}));
+
+  ASSERT_EQ(profile_verdicts_merged.level(), Rule::Level::kBlock);
+  EXPECT_EQ(profile_verdicts_merged.triggered_rules().size(), 1u);
+
+  EXPECT_TRUE(profile_verdicts_merged.triggered_rules().count(kIdenticalKey));
+  EXPECT_EQ(profile_verdicts_merged.triggered_rules().at(kIdenticalKey).rule_id,
+            "rule_id_2");
+  EXPECT_EQ(
+      profile_verdicts_merged.triggered_rules().at(kIdenticalKey).rule_name,
+      "rule_name_2");
 }
 
 TEST(DataControlVerdictTest, MergedCopyWarningVerdictsTriggeredRules) {
@@ -205,41 +274,41 @@
   // internally duplicate the rule in two.
   auto merged_warnings = Verdict::MergePasteVerdicts(Warn(), Warn());
   EXPECT_EQ(merged_warnings.triggered_rules().size(), 1u);
-  EXPECT_TRUE(merged_warnings.triggered_rules().count(kWarnRuleIndex));
-  EXPECT_EQ(merged_warnings.triggered_rules().at(kWarnRuleIndex).rule_id,
+  EXPECT_TRUE(merged_warnings.triggered_rules().count(kWarnRuleKey));
+  EXPECT_EQ(merged_warnings.triggered_rules().at(kWarnRuleKey).rule_id,
             kWarnRuleID);
-  EXPECT_EQ(merged_warnings.triggered_rules().at(kWarnRuleIndex).rule_name,
+  EXPECT_EQ(merged_warnings.triggered_rules().at(kWarnRuleKey).rule_name,
             kWarnRuleName);
 
   // Triggered rules in the OS clipboard verdict are only kept if it's a
   // warning.
   auto report_and_warn = Verdict::MergeCopyWarningVerdicts(Report(), Warn());
   EXPECT_EQ(report_and_warn.triggered_rules().size(), 2u);
-  EXPECT_TRUE(report_and_warn.triggered_rules().count(kReportRuleIndex));
-  EXPECT_TRUE(report_and_warn.triggered_rules().count(kWarnRuleIndex));
-  EXPECT_EQ(report_and_warn.triggered_rules().at(kReportRuleIndex).rule_id,
+  EXPECT_TRUE(report_and_warn.triggered_rules().count(kReportRuleKey));
+  EXPECT_TRUE(report_and_warn.triggered_rules().count(kWarnRuleKey));
+  EXPECT_EQ(report_and_warn.triggered_rules().at(kReportRuleKey).rule_id,
             kReportRuleID);
-  EXPECT_EQ(report_and_warn.triggered_rules().at(kReportRuleIndex).rule_name,
+  EXPECT_EQ(report_and_warn.triggered_rules().at(kReportRuleKey).rule_name,
             kReportRuleName);
-  EXPECT_EQ(report_and_warn.triggered_rules().at(kWarnRuleIndex).rule_id,
+  EXPECT_EQ(report_and_warn.triggered_rules().at(kWarnRuleKey).rule_id,
             kWarnRuleID);
-  EXPECT_EQ(report_and_warn.triggered_rules().at(kWarnRuleIndex).rule_name,
+  EXPECT_EQ(report_and_warn.triggered_rules().at(kWarnRuleKey).rule_name,
             kWarnRuleName);
 
   auto warn_and_block = Verdict::MergeCopyWarningVerdicts(Warn(), Block());
   EXPECT_EQ(warn_and_block.triggered_rules().size(), 1u);
-  EXPECT_TRUE(warn_and_block.triggered_rules().count(kWarnRuleIndex));
-  EXPECT_EQ(warn_and_block.triggered_rules().at(kWarnRuleIndex).rule_id,
+  EXPECT_TRUE(warn_and_block.triggered_rules().count(kWarnRuleKey));
+  EXPECT_EQ(warn_and_block.triggered_rules().at(kWarnRuleKey).rule_id,
             kWarnRuleID);
-  EXPECT_EQ(warn_and_block.triggered_rules().at(kWarnRuleIndex).rule_name,
+  EXPECT_EQ(warn_and_block.triggered_rules().at(kWarnRuleKey).rule_name,
             kWarnRuleName);
 
   auto warn_and_report = Verdict::MergeCopyWarningVerdicts(Warn(), Report());
   EXPECT_EQ(warn_and_report.triggered_rules().size(), 1u);
-  EXPECT_TRUE(warn_and_report.triggered_rules().count(kWarnRuleIndex));
-  EXPECT_EQ(warn_and_report.triggered_rules().at(kWarnRuleIndex).rule_id,
+  EXPECT_TRUE(warn_and_report.triggered_rules().count(kWarnRuleKey));
+  EXPECT_EQ(warn_and_report.triggered_rules().at(kWarnRuleKey).rule_id,
             kWarnRuleID);
-  EXPECT_EQ(warn_and_report.triggered_rules().at(kWarnRuleIndex).rule_name,
+  EXPECT_EQ(warn_and_report.triggered_rules().at(kWarnRuleKey).rule_name,
             kWarnRuleName);
 }
 
diff --git a/components/facilitated_payments/core/browser/BUILD.gn b/components/facilitated_payments/core/browser/BUILD.gn
index 26c27456..e30b84e 100644
--- a/components/facilitated_payments/core/browser/BUILD.gn
+++ b/components/facilitated_payments/core/browser/BUILD.gn
@@ -19,8 +19,6 @@
     "network_api/facilitated_payments_initiate_payment_request_details.h",
     "network_api/facilitated_payments_initiate_payment_response_details.cc",
     "network_api/facilitated_payments_initiate_payment_response_details.h",
-    "network_api/facilitated_payments_network_interface.cc",
-    "network_api/facilitated_payments_network_interface.h",
     "network_api/get_details_for_pix_account_linking_request.cc",
     "network_api/get_details_for_pix_account_linking_request.h",
     "network_api/multiple_request_facilitated_payments_network_interface.cc",
@@ -56,7 +54,6 @@
     "facilitated_payments_driver_unittest.cc",
     "network_api/facilitated_payments_initiate_payment_request_details_unittest.cc",
     "network_api/facilitated_payments_initiate_payment_request_unittest.cc",
-    "network_api/facilitated_payments_network_interface_unittest.cc",
     "network_api/get_details_for_pix_account_linking_request_unittest.cc",
     "network_api/multiple_request_facilitated_payments_network_interface_unittest.cc",
     "payment_link_manager_unittest.cc",
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_client.h b/components/facilitated_payments/core/browser/facilitated_payments_client.h
index 40e12fd..ed63e0d0 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_client.h
+++ b/components/facilitated_payments/core/browser/facilitated_payments_client.h
@@ -40,7 +40,6 @@
 namespace payments::facilitated {
 
 class PixAccountLinkingManager;
-class FacilitatedPaymentsNetworkInterface;
 class MultipleRequestFacilitatedPaymentsNetworkInterface;
 
 // A cross-platform client interface for showing UI for non-form based FOPs.
@@ -58,21 +57,8 @@
 
   // Gets the `FacilitatedPaymentsNetworkInterface` instance owned by the client
   // used for making payment requests. It can be null if the browser context
-  // associated with the WebContents is null. See comment for below function
-  // too.
-  virtual FacilitatedPaymentsNetworkInterface*
-  GetFacilitatedPaymentsNetworkInterface() = 0;
-
-  // Same as above. However this network interface can support multiple active
-  // requests at a time. Sending a request will not affect other ongoing
-  // requests. This is a complete upgrade of the
-  // `FacilitatedPaymentsNetworkInterface` so all new flows should use this
-  // function. All existing flows should be migrated to this. Note that since
-  // each flow should migrate in its own effort, we would need to keep these
-  // functions separate, instead of updating the logic inside
-  // GetFacilitatedPaymentsNetworkInterface. When all migrations are finished,
-  // above function and the FacilitatedPaymentsNetworkInterface class should be
-  // cleaned up.
+  // associated with the WebContents is null. Support multiple active
+  // requests at a time.
   virtual MultipleRequestFacilitatedPaymentsNetworkInterface*
   GetMultipleRequestFacilitatedPaymentsNetworkInterface() = 0;
 
diff --git a/components/facilitated_payments/core/browser/mock_facilitated_payments_client.h b/components/facilitated_payments/core/browser/mock_facilitated_payments_client.h
index 4f791ce..e09502e 100644
--- a/components/facilitated_payments/core/browser/mock_facilitated_payments_client.h
+++ b/components/facilitated_payments/core/browser/mock_facilitated_payments_client.h
@@ -29,8 +29,6 @@
 
 namespace payments::facilitated {
 
-class FacilitatedPaymentsNetworkInterface;
-
 // A mock for the facilitated payment "client" interface.
 class MockFacilitatedPaymentsClient : public FacilitatedPaymentsClient {
  public:
@@ -49,10 +47,6 @@
               GetPaymentsDataManager,
               (),
               (override));
-  MOCK_METHOD(FacilitatedPaymentsNetworkInterface*,
-              GetFacilitatedPaymentsNetworkInterface,
-              (),
-              (override));
   MOCK_METHOD(MultipleRequestFacilitatedPaymentsNetworkInterface*,
               GetMultipleRequestFacilitatedPaymentsNetworkInterface,
               (),
diff --git a/components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request.cc b/components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request.cc
index 7f8470c..24dbf8c 100644
--- a/components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request.cc
+++ b/components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request.cc
@@ -31,8 +31,8 @@
     FacilitatedPaymentsInitiatePaymentRequest(
         std::unique_ptr<FacilitatedPaymentsInitiatePaymentRequestDetails>
             request_details,
-        FacilitatedPaymentsNetworkInterface::InitiatePaymentResponseCallback
-            response_callback,
+        MultipleRequestFacilitatedPaymentsNetworkInterface::
+            InitiatePaymentResponseCallback response_callback,
         const std::string& app_locale,
         const bool full_sync_enabled)
     : request_details_(std::move(request_details)),
diff --git a/components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request.h b/components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request.h
index de9ac16..7bb151c 100644
--- a/components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request.h
+++ b/components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request.h
@@ -12,7 +12,7 @@
 #include "components/autofill/core/browser/payments/payments_requests/payments_request.h"
 #include "components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request_details.h"
 #include "components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_response_details.h"
-#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.h"
+#include "components/facilitated_payments/core/browser/network_api/multiple_request_facilitated_payments_network_interface.h"
 
 namespace payments::facilitated {
 
@@ -28,8 +28,8 @@
   FacilitatedPaymentsInitiatePaymentRequest(
       std::unique_ptr<FacilitatedPaymentsInitiatePaymentRequestDetails>
           request_details,
-      FacilitatedPaymentsNetworkInterface::InitiatePaymentResponseCallback
-          response_callback,
+      MultipleRequestFacilitatedPaymentsNetworkInterface::
+          InitiatePaymentResponseCallback response_callback,
       const std::string& app_locale,
       const bool full_sync_enabled);
   FacilitatedPaymentsInitiatePaymentRequest(
@@ -68,8 +68,8 @@
       request_details_;
   std::unique_ptr<FacilitatedPaymentsInitiatePaymentResponseDetails>
       response_details_;
-  FacilitatedPaymentsNetworkInterface::InitiatePaymentResponseCallback
-      response_callback_;
+  MultipleRequestFacilitatedPaymentsNetworkInterface::
+      InitiatePaymentResponseCallback response_callback_;
   std::string app_locale_;
   const bool full_sync_enabled_;
 };
diff --git a/components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.cc b/components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.cc
deleted file mode 100644
index f8d598b..0000000
--- a/components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.h"
-
-#include "components/autofill/core/browser/payments/account_info_getter.h"
-#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request.h"
-
-namespace payments::facilitated {
-
-FacilitatedPaymentsNetworkInterface::FacilitatedPaymentsNetworkInterface(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    signin::IdentityManager* identity_manager,
-    autofill::AccountInfoGetter* account_info_getter,
-    bool is_off_the_record)
-    : PaymentsNetworkInterfaceBase(url_loader_factory,
-                                   identity_manager,
-                                   account_info_getter,
-                                   is_off_the_record) {}
-
-FacilitatedPaymentsNetworkInterface::~FacilitatedPaymentsNetworkInterface() =
-    default;
-
-void FacilitatedPaymentsNetworkInterface::InitiatePayment(
-    std::unique_ptr<FacilitatedPaymentsInitiatePaymentRequestDetails>
-        request_details,
-    InitiatePaymentResponseCallback response_callback,
-    const std::string& app_locale) {
-  IssueRequest(std::make_unique<FacilitatedPaymentsInitiatePaymentRequest>(
-      std::move(request_details), std::move(response_callback), app_locale,
-      account_info_getter_->IsSyncFeatureEnabledForPaymentsServerMetrics()));
-}
-
-}  // namespace payments::facilitated
diff --git a/components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.h b/components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.h
deleted file mode 100644
index fd12b64..0000000
--- a/components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_FACILITATED_PAYMENTS_CORE_BROWSER_NETWORK_API_FACILITATED_PAYMENTS_NETWORK_INTERFACE_H_
-#define COMPONENTS_FACILITATED_PAYMENTS_CORE_BROWSER_NETWORK_API_FACILITATED_PAYMENTS_NETWORK_INTERFACE_H_
-
-#include <memory>
-
-#include "components/autofill/core/browser/payments/payments_autofill_client.h"
-#include "components/autofill/core/browser/payments/payments_network_interface_base.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-
-namespace signin {
-class IdentityManager;
-}  // namespace signin
-
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace payments::facilitated {
-
-class FacilitatedPaymentsInitiatePaymentRequestDetails;
-class FacilitatedPaymentsInitiatePaymentResponseDetails;
-
-// Issues Payments RPCs and manages responses and failure conditions for
-// Facilitated Payments. Only one request may be active at a time. Initiating a
-// new request will cancel a pending request.
-class FacilitatedPaymentsNetworkInterface
-    : public autofill::payments::PaymentsNetworkInterfaceBase {
- public:
-  using InitiatePaymentResponseCallback = base::OnceCallback<void(
-      autofill::payments::PaymentsAutofillClient::PaymentsRpcResult,
-      std::unique_ptr<FacilitatedPaymentsInitiatePaymentResponseDetails>)>;
-
-  FacilitatedPaymentsNetworkInterface(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      signin::IdentityManager* identity_manager,
-      autofill::AccountInfoGetter* account_info_getter,
-      bool is_off_the_record = false);
-
-  FacilitatedPaymentsNetworkInterface(
-      const FacilitatedPaymentsNetworkInterface&) = delete;
-  FacilitatedPaymentsNetworkInterface& operator=(
-      const FacilitatedPaymentsNetworkInterface&) = delete;
-
-  ~FacilitatedPaymentsNetworkInterface() override;
-
-  // Makes a `FacilitatedPaymentsInitiatePaymentRequest` to the Payments server.
-  // This method is virtual so it can be overridden in tests.
-  virtual void InitiatePayment(
-      std::unique_ptr<FacilitatedPaymentsInitiatePaymentRequestDetails>
-          request_details,
-      InitiatePaymentResponseCallback response_callback,
-      const std::string& app_locale);
-};
-
-}  // namespace payments::facilitated
-
-#endif  // COMPONENTS_FACILITATED_PAYMENTS_CORE_BROWSER_NETWORK_API_FACILITATED_PAYMENTS_NETWORK_INTERFACE_H_
diff --git a/components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface_unittest.cc b/components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface_unittest.cc
deleted file mode 100644
index c8f686f..0000000
--- a/components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface_unittest.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.h"
-
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "components/autofill/core/browser/payments/payments_autofill_client.h"
-#include "components/autofill/core/browser/payments/payments_network_interface_test_base.h"
-#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request_details.h"
-#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_response_details.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace payments::facilitated {
-
-class FacilitatedPaymentsNetworkInterfaceTest
-    : public autofill::payments::PaymentsNetworkInterfaceTestBase,
-      public testing::Test {
- public:
-  FacilitatedPaymentsNetworkInterfaceTest() = default;
-
-  FacilitatedPaymentsNetworkInterfaceTest(
-      const FacilitatedPaymentsNetworkInterfaceTest&) = delete;
-  FacilitatedPaymentsNetworkInterfaceTest& operator=(
-      const FacilitatedPaymentsNetworkInterfaceTest&) = delete;
-
-  ~FacilitatedPaymentsNetworkInterfaceTest() override = default;
-
-  void SetUp() override {
-    SetUpTest();
-    payments_network_interface_ =
-        std::make_unique<FacilitatedPaymentsNetworkInterface>(
-            test_shared_loader_factory_, identity_test_env_.identity_manager(),
-            &test_personal_data_.payments_data_manager());
-  }
-
-  void TearDown() override { payments_network_interface_.reset(); }
-
- protected:
-  void SendInitiatePaymentRequest() {
-    auto request_details =
-        std::make_unique<FacilitatedPaymentsInitiatePaymentRequestDetails>();
-    request_details->risk_data_ = "seems pretty risky";
-    request_details->client_token_ =
-        std::vector<uint8_t>{'t', 'o', 'k', 'e', 'n'};
-    request_details->billing_customer_number_ = 11;
-    request_details->merchant_payment_page_hostname_ = "foo.com";
-    request_details->instrument_id_ = 13;
-    request_details->pix_code_ = "a valid code";
-    payments_network_interface_->InitiatePayment(
-        std::move(request_details),
-        base::BindOnce(&FacilitatedPaymentsNetworkInterfaceTest::
-                           OnInitiatePaymentResponseReceived,
-                       GetWeakPtr()),
-        "language-LOCALE");
-  }
-
-  std::unique_ptr<FacilitatedPaymentsInitiatePaymentResponseDetails>
-      response_details_;
-  std::unique_ptr<FacilitatedPaymentsNetworkInterface>
-      payments_network_interface_;
-
- private:
-  void OnInitiatePaymentResponseReceived(
-      autofill::payments::PaymentsAutofillClient::PaymentsRpcResult result,
-      std::unique_ptr<FacilitatedPaymentsInitiatePaymentResponseDetails>
-          response_details) {
-    result_ = result;
-    response_details_ = std::move(response_details);
-  }
-
-  base::WeakPtr<FacilitatedPaymentsNetworkInterfaceTest> GetWeakPtr() {
-    return weak_ptr_factory_.GetWeakPtr();
-  }
-
-  base::WeakPtrFactory<FacilitatedPaymentsNetworkInterfaceTest>
-      weak_ptr_factory_{this};
-};
-
-TEST_F(FacilitatedPaymentsNetworkInterfaceTest,
-       InitiatePaymentRequest_Success) {
-  SendInitiatePaymentRequest();
-  IssueOAuthToken();
-  ReturnResponse(
-      payments_network_interface_.get(), net::HTTP_OK,
-      "{\"trigger_purchase_manager\":{\"secure_payload\":{\"opaque_token\":"
-      "\"dG9rZW4=\",\"secure_data\":[{\"key\":1,\"value\":\"secure_data_"
-      "value\"}]}}}");
-
-  // Verify the request contains necessary info like the payment details, and
-  // the instrument id.
-  EXPECT_TRUE(GetUploadData().find("payment_details") != std::string::npos);
-  EXPECT_TRUE(GetUploadData().find("sender_instrument_id") !=
-              std::string::npos);
-
-  // Verify that a success result was received because the response contained
-  // the action token.
-  EXPECT_EQ(
-      autofill::payments::PaymentsAutofillClient::PaymentsRpcResult::kSuccess,
-      result_);
-  std::vector<uint8_t> expected_action_token = {'t', 'o', 'k', 'e', 'n'};
-  EXPECT_EQ(expected_action_token,
-            response_details_->secure_payload_.action_token);
-}
-
-TEST_F(FacilitatedPaymentsNetworkInterfaceTest,
-       InitiatePaymentRequest_Failure) {
-  SendInitiatePaymentRequest();
-  IssueOAuthToken();
-  ReturnResponse(
-      payments_network_interface_.get(), net::HTTP_OK,
-      "{\"error\":{\"user_error_message\":\"Something went wrong!\"}}");
-
-  // Verify the request contains necessary info like the payment details, and
-  // the instrument id.
-  EXPECT_TRUE(GetUploadData().find("payment_details") != std::string::npos);
-  EXPECT_TRUE(GetUploadData().find("sender_instrument_id") !=
-              std::string::npos);
-
-  // Verify that a failure result was received because the response contained
-  // error.
-  EXPECT_EQ(autofill::payments::PaymentsAutofillClient::PaymentsRpcResult::
-                kPermanentFailure,
-            result_);
-  EXPECT_EQ("Something went wrong!", response_details_->error_message_.value());
-}
-
-}  // namespace payments::facilitated
diff --git a/components/facilitated_payments/core/browser/network_api/get_details_for_pix_account_linking_request.h b/components/facilitated_payments/core/browser/network_api/get_details_for_pix_account_linking_request.h
index 5a79d50..c1345cd 100644
--- a/components/facilitated_payments/core/browser/network_api/get_details_for_pix_account_linking_request.h
+++ b/components/facilitated_payments/core/browser/network_api/get_details_for_pix_account_linking_request.h
@@ -11,7 +11,7 @@
 #include "base/values.h"
 #include "components/autofill/core/browser/payments/payments_autofill_client.h"
 #include "components/autofill/core/browser/payments/payments_requests/payments_request.h"
-#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.h"
+#include "components/facilitated_payments/core/browser/network_api/multiple_request_facilitated_payments_network_interface.h"
 
 namespace payments::facilitated {
 
diff --git a/components/facilitated_payments/core/browser/network_api/mock_facilitated_payments_network_interface.cc b/components/facilitated_payments/core/browser/network_api/mock_facilitated_payments_network_interface.cc
index b0b5ab4..eac6a04b 100644
--- a/components/facilitated_payments/core/browser/network_api/mock_facilitated_payments_network_interface.cc
+++ b/components/facilitated_payments/core/browser/network_api/mock_facilitated_payments_network_interface.cc
@@ -4,20 +4,10 @@
 
 #include "components/facilitated_payments/core/browser/network_api/mock_facilitated_payments_network_interface.h"
 
-#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.h"
 #include "components/facilitated_payments/core/browser/network_api/multiple_request_facilitated_payments_network_interface.h"
 
 namespace payments::facilitated {
 
-MockFacilitatedPaymentsNetworkInterface::
-    MockFacilitatedPaymentsNetworkInterface()
-    : FacilitatedPaymentsNetworkInterface(/*url_loader_factory=*/nullptr,
-                                          /*identity_manager=*/nullptr,
-                                          /*account_info_getter=*/nullptr) {}
-
-MockFacilitatedPaymentsNetworkInterface::
-    ~MockFacilitatedPaymentsNetworkInterface() = default;
-
 MockMultipleRequestFacilitatedPaymentsNetworkInterface::
     MockMultipleRequestFacilitatedPaymentsNetworkInterface(
         signin::IdentityManager& identity_manager,
diff --git a/components/facilitated_payments/core/browser/network_api/mock_facilitated_payments_network_interface.h b/components/facilitated_payments/core/browser/network_api/mock_facilitated_payments_network_interface.h
index deb27c1b..5c3c9c69f 100644
--- a/components/facilitated_payments/core/browser/network_api/mock_facilitated_payments_network_interface.h
+++ b/components/facilitated_payments/core/browser/network_api/mock_facilitated_payments_network_interface.h
@@ -8,30 +8,11 @@
 #include <memory>
 
 #include "components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request_details.h"
-#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.h"
 #include "components/facilitated_payments/core/browser/network_api/multiple_request_facilitated_payments_network_interface.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace payments::facilitated {
 
-class MockFacilitatedPaymentsNetworkInterface
-    : public FacilitatedPaymentsNetworkInterface {
- public:
-  using RequestId =
-      base::StrongAlias<struct autofill::payments::RequestIdTag, std::string>;
-
-  MockFacilitatedPaymentsNetworkInterface();
-  ~MockFacilitatedPaymentsNetworkInterface() override;
-
-  MOCK_METHOD(
-      void,
-      InitiatePayment,
-      (std::unique_ptr<FacilitatedPaymentsInitiatePaymentRequestDetails>,
-       InitiatePaymentResponseCallback,
-       const std::string&),
-      (override));
-};
-
 class MockMultipleRequestFacilitatedPaymentsNetworkInterface
     : public MultipleRequestFacilitatedPaymentsNetworkInterface {
  public:
diff --git a/components/facilitated_payments/core/browser/payment_link_manager.cc b/components/facilitated_payments/core/browser/payment_link_manager.cc
index a55fd61e..73a0024 100644
--- a/components/facilitated_payments/core/browser/payment_link_manager.cc
+++ b/components/facilitated_payments/core/browser/payment_link_manager.cc
@@ -23,7 +23,7 @@
 #include "components/facilitated_payments/core/browser/facilitated_payments_client.h"
 #include "components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request_details.h"
 #include "components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_response_details.h"
-#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.h"
+#include "components/facilitated_payments/core/browser/network_api/multiple_request_facilitated_payments_network_interface.h"
 #include "components/facilitated_payments/core/browser/payment_link_manager.h"
 #include "components/facilitated_payments/core/browser/strike_databases/payment_link_suggestion_strike_database.h"
 #include "components/facilitated_payments/core/features/features.h"
@@ -338,8 +338,9 @@
 }
 
 void PaymentLinkManager::SendInitiatePaymentRequest() {
-  FacilitatedPaymentsNetworkInterface* payments_network_interface =
-      client_->GetFacilitatedPaymentsNetworkInterface();
+  MultipleRequestFacilitatedPaymentsNetworkInterface*
+      payments_network_interface =
+          client_->GetMultipleRequestFacilitatedPaymentsNetworkInterface();
 
   if (!payments_network_interface) {
     ShowErrorScreen();
diff --git a/components/facilitated_payments/core/browser/payment_link_manager_unittest.cc b/components/facilitated_payments/core/browser/payment_link_manager_unittest.cc
index d76de1d4..7f8a157 100644
--- a/components/facilitated_payments/core/browser/payment_link_manager_unittest.cc
+++ b/components/facilitated_payments/core/browser/payment_link_manager_unittest.cc
@@ -33,6 +33,7 @@
 #include "components/facilitated_payments/core/utils/facilitated_payments_ui_utils.h"
 #include "components/optimization_guide/core/hints/mock_optimization_guide_decider.h"
 #include "components/signin/public/identity_manager/account_info.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "components/sync/test/test_sync_service.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "google_apis/gaia/gaia_id.h"
@@ -77,10 +78,13 @@
     payments_data_manager_.SetPrefService(pref_service_.get());
     payments_data_manager_.SetSyncServiceForTest(&sync_service_);
     test_strike_database_ = std::make_unique<autofill::TestStrikeDatabase>();
+    payments_network_interface_ = std::make_unique<
+        MockMultipleRequestFacilitatedPaymentsNetworkInterface>(
+        *identity_test_env_.identity_manager(), payments_data_manager_);
     ON_CALL(client_, GetPaymentsDataManager)
         .WillByDefault(testing::Return(&payments_data_manager_));
-    ON_CALL(client_, GetFacilitatedPaymentsNetworkInterface)
-        .WillByDefault(testing::Return(&payments_network_interface_));
+    ON_CALL(client_, GetMultipleRequestFacilitatedPaymentsNetworkInterface)
+        .WillByDefault(testing::Return(payments_network_interface_.get()));
     ON_CALL(client_, IsInLandscapeMode).WillByDefault(testing::Return(false));
     ON_CALL(client_, IsFoldable).WillByDefault(testing::Return(false));
     ON_CALL(client_, GetCoreAccountInfo)
@@ -120,6 +124,7 @@
  protected:
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  signin::IdentityTestEnvironment identity_test_env_;
   MockFacilitatedPaymentsClient client_;
   optimization_guide::MockOptimizationGuideDecider optimization_guide_decider_;
   // Order matters here because `payment_link_manager_` keeps a reference
@@ -128,7 +133,8 @@
   std::unique_ptr<PrefService> pref_service_;
   syncer::TestSyncService sync_service_;
   autofill::TestPaymentsDataManager payments_data_manager_;
-  MockFacilitatedPaymentsNetworkInterface payments_network_interface_;
+  std::unique_ptr<MockMultipleRequestFacilitatedPaymentsNetworkInterface>
+      payments_network_interface_;
   ukm::TestAutoSetUkmRecorder ukm_recorder_;
   std::unique_ptr<autofill::TestStrikeDatabase> test_strike_database_;
 };
@@ -639,11 +645,11 @@
 TEST_F(
     PaymentLinkManagerTest,
     SendInitiatePaymentRequest_PaymentsNetworkInterfaceNotAvailable_InitiatePaymentNotTriggered) {
-  EXPECT_CALL(client_, GetFacilitatedPaymentsNetworkInterface)
+  EXPECT_CALL(client_, GetMultipleRequestFacilitatedPaymentsNetworkInterface)
       .Times(1)
       .WillOnce(testing::Return(nullptr));
 
-  EXPECT_CALL(payments_network_interface_,
+  EXPECT_CALL(*payments_network_interface_,
               InitiatePayment(testing::_, testing::_, testing::_))
       .Times(0);
   EXPECT_CALL(client_, ShowErrorScreen);
@@ -654,7 +660,7 @@
 // Test that LogInitiatePaymentAttempt is logged correctly.
 TEST_F(PaymentLinkManagerTest, LogInitiatePaymentAttempt) {
   base::HistogramTester histogram_tester;
-  EXPECT_CALL(payments_network_interface_,
+  EXPECT_CALL(*payments_network_interface_,
               InitiatePayment(testing::_, testing::_, testing::_));
 
   test_api(*payment_link_manager_).SendInitiatePaymentRequest();
diff --git a/components/facilitated_payments/core/browser/pix_manager.cc b/components/facilitated_payments/core/browser/pix_manager.cc
index c3c2fb40..2dfba53 100644
--- a/components/facilitated_payments/core/browser/pix_manager.cc
+++ b/components/facilitated_payments/core/browser/pix_manager.cc
@@ -15,7 +15,6 @@
 #include "components/autofill/core/browser/data_model/payments/bank_account.h"
 #include "components/autofill/core/browser/payments/payments_util.h"
 #include "components/facilitated_payments/core/browser/facilitated_payments_client.h"
-#include "components/facilitated_payments/core/browser/network_api/facilitated_payments_network_interface.h"
 #include "components/facilitated_payments/core/browser/network_api/multiple_request_facilitated_payments_network_interface.h"
 #include "components/facilitated_payments/core/features/features.h"
 #include "components/facilitated_payments/core/metrics/facilitated_payments_metrics.h"
@@ -259,29 +258,14 @@
 }
 
 void PixManager::SendInitiatePaymentRequest() {
-  if (base::FeatureList::IsEnabled(
-          kSupportMultipleServerRequestsForPixPayments)) {
-    if (auto* payments_network_interface =
-            client_->GetMultipleRequestFacilitatedPaymentsNetworkInterface()) {
-      LogInitiatePaymentAttempt(kPaymentsType);
-      payments_network_interface->InitiatePayment(
-          std::move(initiate_payment_request_details_),
-          base::BindOnce(&PixManager::OnInitiatePaymentResponseReceived,
-                         weak_ptr_factory_.GetWeakPtr(),
-                         base::TimeTicks::Now()),
-          client_->GetPaymentsDataManager()->app_locale());
-    }
-  } else {
-    if (auto* payments_network_interface =
-            client_->GetFacilitatedPaymentsNetworkInterface()) {
-      LogInitiatePaymentAttempt(kPaymentsType);
-      payments_network_interface->InitiatePayment(
-          std::move(initiate_payment_request_details_),
-          base::BindOnce(&PixManager::OnInitiatePaymentResponseReceived,
-                         weak_ptr_factory_.GetWeakPtr(),
-                         base::TimeTicks::Now()),
-          client_->GetPaymentsDataManager()->app_locale());
-    }
+  if (auto* payments_network_interface =
+          client_->GetMultipleRequestFacilitatedPaymentsNetworkInterface()) {
+    LogInitiatePaymentAttempt(kPaymentsType);
+    payments_network_interface->InitiatePayment(
+        std::move(initiate_payment_request_details_),
+        base::BindOnce(&PixManager::OnInitiatePaymentResponseReceived,
+                       weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now()),
+        client_->GetPaymentsDataManager()->app_locale());
   }
 }
 
diff --git a/components/facilitated_payments/core/browser/pix_manager_unittest.cc b/components/facilitated_payments/core/browser/pix_manager_unittest.cc
index 8fcbbd1..21d54ca43 100644
--- a/components/facilitated_payments/core/browser/pix_manager_unittest.cc
+++ b/components/facilitated_payments/core/browser/pix_manager_unittest.cc
@@ -1305,17 +1305,10 @@
 }
 
 // Test the PixManager works with the FacilitatedPaymentsNetworkInterface
-// correctly. Param denotes whether it uses the multiple request version of the
-// interface.
-class PixManagerPaymentsNetworkInterfaceTest
-    : public PixManagerTest,
-      public testing::WithParamInterface<bool> {
+// correctly.
+class PixManagerPaymentsNetworkInterfaceTest : public PixManagerTest {
  public:
   PixManagerPaymentsNetworkInterfaceTest() {
-    scoped_feature_list_.InitWithFeatureState(
-        kSupportMultipleServerRequestsForPixPayments, GetParam());
-    payments_network_interface_ =
-        std::make_unique<MockFacilitatedPaymentsNetworkInterface>();
     multiple_request_payments_network_interface_ = std::make_unique<
         MockMultipleRequestFacilitatedPaymentsNetworkInterface>(
         *identity_test_env_.identity_manager(), *payments_data_manager_);
@@ -1323,41 +1316,25 @@
 
   void SetUp() override {
     PixManagerTest::SetUp();
-    ON_CALL(*client_, GetFacilitatedPaymentsNetworkInterface)
-        .WillByDefault(testing::Return(payments_network_interface_.get()));
     ON_CALL(*client_, GetMultipleRequestFacilitatedPaymentsNetworkInterface)
         .WillByDefault(testing::Return(
             multiple_request_payments_network_interface_.get()));
   }
 
  protected:
-  bool IsUsingMultipleRequestsInterface() const { return GetParam(); }
-
-  std::unique_ptr<MockFacilitatedPaymentsNetworkInterface>
-      payments_network_interface_;
   std::unique_ptr<MockMultipleRequestFacilitatedPaymentsNetworkInterface>
       multiple_request_payments_network_interface_;
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
   signin::IdentityTestEnvironment identity_test_env_;
 };
 
-INSTANTIATE_TEST_SUITE_P(,
-                         PixManagerPaymentsNetworkInterfaceTest,
-                         testing::Bool());
-
 // Test that SendInitiatePaymentRequest initiates payment using the
 // FacilitatedPaymentsNetworkInterface.
-TEST_P(PixManagerPaymentsNetworkInterfaceTest, SendInitiatePaymentRequest) {
+TEST_F(PixManagerPaymentsNetworkInterfaceTest, SendInitiatePaymentRequest) {
   base::HistogramTester histogram_tester;
-  if (IsUsingMultipleRequestsInterface()) {
-    EXPECT_CALL(*multiple_request_payments_network_interface_,
-                InitiatePayment(testing::_, testing::_, testing::_));
-  } else {
-    EXPECT_CALL(*payments_network_interface_,
-                InitiatePayment(testing::_, testing::_, testing::_));
-  }
+  EXPECT_CALL(*multiple_request_payments_network_interface_,
+              InitiatePayment(testing::_, testing::_, testing::_));
 
   pix_manager_->SendInitiatePaymentRequest();
 
@@ -1371,7 +1348,7 @@
 // `FacilitatedPaymentsNetworkInterface::InitiatePayment` call has failure
 // result, purchase action is not invoked. Instead, an error message is
 // shown.
-TEST_P(PixManagerPaymentsNetworkInterfaceTest,
+TEST_F(PixManagerPaymentsNetworkInterfaceTest,
        OnInitiatePaymentResponseReceived_FailureResponse) {
   base::HistogramTester histogram_tester;
   pix_manager_->SendInitiatePaymentRequest();
@@ -1403,7 +1380,7 @@
 // Test that if the response from
 // `FacilitatedPaymentsNetworkInterface::InitiatePayment` has empty action
 // token, purchase action is not invoked. Instead, an error message is shown.
-TEST_P(PixManagerPaymentsNetworkInterfaceTest,
+TEST_F(PixManagerPaymentsNetworkInterfaceTest,
        OnInitiatePaymentResponseReceived_NoActionToken_ErrorScreenShown) {
   base::HistogramTester histogram_tester;
   pix_manager_->SendInitiatePaymentRequest();
@@ -1432,7 +1409,7 @@
 
 // Test that if the core account is std::nullopt, purchase action is not
 // invoked. Instead, an error message is shown.
-TEST_P(PixManagerPaymentsNetworkInterfaceTest,
+TEST_F(PixManagerPaymentsNetworkInterfaceTest,
        OnInitiatePaymentResponseReceived_NoCoreAccountInfo_ErrorScreenShown) {
   base::HistogramTester histogram_tester;
   pix_manager_->SendInitiatePaymentRequest();
@@ -1462,7 +1439,7 @@
 
 // Test that if the user is logged out, purchase action is not invoked.
 // Instead, an error message is shown.
-TEST_P(PixManagerPaymentsNetworkInterfaceTest,
+TEST_F(PixManagerPaymentsNetworkInterfaceTest,
        OnInitiatePaymentResponseReceived_LoggedOutProfile_ErrorScreenShown) {
   base::HistogramTester histogram_tester;
   pix_manager_->SendInitiatePaymentRequest();
@@ -1492,7 +1469,7 @@
 
 // Test that the purchase action is invoked after receiving a success response
 // from the `FacilitatedPaymentsNetworkInterface::InitiatePayment` call.
-TEST_P(PixManagerPaymentsNetworkInterfaceTest,
+TEST_F(PixManagerPaymentsNetworkInterfaceTest,
        OnInitiatePaymentResponseReceived_InvokePurchaseActionTriggered) {
   base::HistogramTester histogram_tester;
   pix_manager_->SendInitiatePaymentRequest();
@@ -1517,12 +1494,8 @@
 
 // Test that refreshing the page will cancel pending initiate payment request
 // callback.
-TEST_P(PixManagerPaymentsNetworkInterfaceTest, Reset) {
-  if (IsUsingMultipleRequestsInterface()) {
-    EXPECT_CALL(*multiple_request_payments_network_interface_, InitiatePayment);
-  } else {
-    EXPECT_CALL(*payments_network_interface_, InitiatePayment);
-  }
+TEST_F(PixManagerPaymentsNetworkInterfaceTest, Reset) {
+  EXPECT_CALL(*multiple_request_payments_network_interface_, InitiatePayment);
 
   pix_manager_->SendInitiatePaymentRequest();
   pix_manager_->Reset();
diff --git a/components/facilitated_payments/core/features/features.cc b/components/facilitated_payments/core/features/features.cc
index dd6a28a..d895c7c 100644
--- a/components/facilitated_payments/core/features/features.cc
+++ b/components/facilitated_payments/core/features/features.cc
@@ -28,13 +28,7 @@
 // When enabled, Chrome will offer to pay with eWallet accounts if a payment
 // link is detected.
 BASE_FEATURE(kEwalletPayments, base::FEATURE_ENABLED_BY_DEFAULT);
-#endif  // BUILDFLAG(IS_ANDROID)
 
-// When enabled, Pix will be able to send multiple server request at a time.
-BASE_FEATURE(kSupportMultipleServerRequestsForPixPayments,
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-#if BUILDFLAG(IS_ANDROID)
 // When enabled, Chrome will offer an app list when a supported payment link is
 // detected. Users can choose the payment app they want to
 // use and be redirected to the chosen app to complete the payment flow.
diff --git a/components/facilitated_payments/core/features/features.h b/components/facilitated_payments/core/features/features.h
index f98994b..5c1c6d6e 100644
--- a/components/facilitated_payments/core/features/features.h
+++ b/components/facilitated_payments/core/features/features.h
@@ -16,9 +16,6 @@
 BASE_DECLARE_FEATURE(kEnablePixAccountLinking);
 BASE_DECLARE_FEATURE(kEnableStaticQrCodeForPix);
 BASE_DECLARE_FEATURE(kEwalletPayments);
-#endif  // BUILDFLAG(IS_ANDROID)
-BASE_DECLARE_FEATURE(kSupportMultipleServerRequestsForPixPayments);
-#if BUILDFLAG(IS_ANDROID)
 BASE_DECLARE_FEATURE(kFacilitatedPaymentsEnableA2APayment);
 #endif  // BUILDFLAG(IS_ANDROID)
 
diff --git a/components/history/core/browser/expire_history_backend.cc b/components/history/core/browser/expire_history_backend.cc
index 63460e5..cf186ce 100644
--- a/components/history/core/browser/expire_history_backend.cc
+++ b/components/history/core/browser/expire_history_backend.cc
@@ -28,6 +28,7 @@
 #include "components/history/core/browser/history_backend_client.h"
 #include "components/history/core/browser/history_backend_notifier.h"
 #include "components/history/core/browser/history_database.h"
+#include "components/history/core/browser/history_types.h"
 
 namespace history {
 
@@ -585,12 +586,15 @@
 
     // Check if there are any other visits for this URL and update the time
     // (the time change may not actually be synced to disk below when we're
-    // archiving).
+    // archiving). This includes 404 visits (see crbug.com/430618428 for
+    // context).
     VisitRow last_visit;
-    if (main_db_->GetMostRecentVisitForURL(url_row.id(), &last_visit))
+    if (main_db_->GetMostRecentVisitForURL(
+            url_row.id(), &last_visit, VisitQuery404sPolicy::kInclude404s)) {
       url_row.set_last_visit(last_visit.visit_time);
-    else
+    } else {
       url_row.set_last_visit(base::Time());
+    }
 
     // Don't delete URLs with visits still in the DB, or pinned.
     bool is_pinned =
diff --git a/components/history/core/browser/expire_history_backend.h b/components/history/core/browser/expire_history_backend.h
index a127595..11cc61b2 100644
--- a/components/history/core/browser/expire_history_backend.h
+++ b/components/history/core/browser/expire_history_backend.h
@@ -212,7 +212,7 @@
   //
   // The visits in the given vector should have already been deleted from the
   // database, and the list of affected URLs already be filled into
-  // `depenencies->affected_urls`.
+  // `dependencies->affected_urls`.
   //
   // Starred URLs will not be deleted. The information in the dependencies that
   // DeleteOneURL fills in will be updated, and this function will also delete
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc
index 9d788180..8d40b20 100644
--- a/components/history/core/browser/history_backend.cc
+++ b/components/history/core/browser/history_backend.cc
@@ -1672,9 +1672,12 @@
   return delegate_->CanAddURL(url);
 }
 
-bool HistoryBackend::GetMostRecentVisitForURL(URLID id, VisitRow* visit_row) {
+bool HistoryBackend::GetMostRecentVisitForURL(
+    URLID id,
+    VisitRow* visit_row,
+    VisitQuery404sPolicy policy_for_404_visits) {
   if (db_)
-    return db_->GetMostRecentVisitForURL(id, visit_row);
+    return db_->GetMostRecentVisitForURL(id, visit_row, policy_for_404_visits);
   return false;
 }
 
@@ -2855,7 +2858,8 @@
     return {};
 
   URLID from_url_id = db_->GetRowForURL(from_url, nullptr);
-  VisitID cur_visit = db_->GetMostRecentVisitForURL(from_url_id, nullptr);
+  VisitID cur_visit = db_->GetMostRecentVisitForURL(
+      from_url_id, nullptr, VisitQuery404sPolicy::kExclude404s);
   if (!cur_visit)
     return {};  // No visits for URL.
 
@@ -2869,7 +2873,9 @@
     return {};
 
   URLID to_url_id = db_->GetRowForURL(to_url, nullptr);
-  VisitID cur_visit = db_->GetMostRecentVisitForURL(to_url_id, nullptr);
+  // TODO: crbug.com/448407141 Take in a 404 policy param and pass in here
+  VisitID cur_visit = db_->GetMostRecentVisitForURL(
+      to_url_id, nullptr, VisitQuery404sPolicy::kInclude404s);
   if (!cur_visit)
     return {};  // No visits for URL.
 
diff --git a/components/history/core/browser/history_backend.h b/components/history/core/browser/history_backend.h
index 7018ed3..29885cf 100644
--- a/components/history/core/browser/history_backend.h
+++ b/components/history/core/browser/history_backend.h
@@ -660,7 +660,10 @@
   // methods.
 
   // TODO(manukh): DEPRECATED (see above comment)
-  bool GetMostRecentVisitForURL(URLID id, VisitRow* visit_row) override;
+  bool GetMostRecentVisitForURL(
+      URLID id,
+      VisitRow* visit_row,
+      VisitQuery404sPolicy policy_for_404_visits) override;
 
   QueryURLAndVisitsResult GetMostRecentVisitsForGurl(
       GURL url,
diff --git a/components/history/core/browser/sync/history_backend_for_sync.h b/components/history/core/browser/sync/history_backend_for_sync.h
index 4b39588..7ea79f6 100644
--- a/components/history/core/browser/sync/history_backend_for_sync.h
+++ b/components/history/core/browser/sync/history_backend_for_sync.h
@@ -28,7 +28,10 @@
   virtual bool GetURLByID(URLID url_id, URLRow* url_row) = 0;
   virtual bool GetVisitByID(VisitID visit_id, VisitRow* visit_row) = 0;
   virtual bool GetVisitSource(const VisitID visit_id, VisitSource* source) = 0;
-  virtual bool GetMostRecentVisitForURL(URLID id, VisitRow* visit_row) = 0;
+  virtual bool GetMostRecentVisitForURL(
+      URLID id,
+      VisitRow* visit_row,
+      VisitQuery404sPolicy policy_for_404_visits) = 0;
   virtual bool GetLastVisitByTime(base::Time visit_time,
                                   VisitRow* visit_row) = 0;
   virtual VisitVector GetRedirectChain(VisitRow visit) = 0;
diff --git a/components/history/core/browser/sync/history_sync_bridge.cc b/components/history/core/browser/sync/history_sync_bridge.cc
index cce7b27..0a356c0 100644
--- a/components/history/core/browser/sync/history_sync_bridge.cc
+++ b/components/history/core/browser/sync/history_sync_bridge.cc
@@ -823,7 +823,8 @@
 
   for (const URLRow& url_row : changed_urls) {
     VisitRow visit_row;
-    if (history_backend_->GetMostRecentVisitForURL(url_row.id(), &visit_row) &&
+    if (history_backend_->GetMostRecentVisitForURL(
+            url_row.id(), &visit_row, VisitQuery404sPolicy::kInclude404s) &&
         visit_row.originator_cache_guid.empty()) {
       // It's the URL corresponding to a local visit - probably the title got
       // updated.
diff --git a/components/history/core/browser/sync/test_history_backend_for_sync.cc b/components/history/core/browser/sync/test_history_backend_for_sync.cc
index a5bb7458..5f3de75 100644
--- a/components/history/core/browser/sync/test_history_backend_for_sync.cc
+++ b/components/history/core/browser/sync/test_history_backend_for_sync.cc
@@ -6,6 +6,7 @@
 
 #include <vector>
 
+#include "components/history/core/browser/history_types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace history {
@@ -135,10 +136,22 @@
   return false;
 }
 
-bool TestHistoryBackendForSync::GetMostRecentVisitForURL(URLID id,
-                                                         VisitRow* visit_row) {
+bool TestHistoryBackendForSync::GetMostRecentVisitForURL(
+    URLID id,
+    VisitRow* visit_row,
+    VisitQuery404sPolicy policy_for_404_visits) {
   *visit_row = VisitRow();
   for (const VisitRow& candidate : visits_) {
+    VisitContextAnnotations context_annotations;
+    if (context_annotations_.count(candidate.visit_id)) {
+      context_annotations = context_annotations_[candidate.visit_id];
+
+      int http_response_code = context_annotations.on_visit.response_code;
+      if (policy_for_404_visits == VisitQuery404sPolicy::kExclude404s &&
+          http_response_code == 404) {
+        continue;
+      }
+    }
     if (candidate.url_id == id &&
         (candidate.visit_time > visit_row->visit_time ||
          (candidate.visit_time == visit_row->visit_time &&
diff --git a/components/history/core/browser/sync/test_history_backend_for_sync.h b/components/history/core/browser/sync/test_history_backend_for_sync.h
index e4d31c1d..e4848e6 100644
--- a/components/history/core/browser/sync/test_history_backend_for_sync.h
+++ b/components/history/core/browser/sync/test_history_backend_for_sync.h
@@ -47,7 +47,10 @@
   bool GetURLByID(URLID url_id, URLRow* url_row) override;
   bool GetVisitByID(VisitID visit_id, VisitRow* visit_row) override;
   bool GetVisitSource(const VisitID visit_id, VisitSource* source) override;
-  bool GetMostRecentVisitForURL(URLID id, VisitRow* visit_row) override;
+  bool GetMostRecentVisitForURL(
+      URLID id,
+      VisitRow* visit_row,
+      VisitQuery404sPolicy policy_for_404_visits) override;
   bool GetLastVisitByTime(base::Time visit_time, VisitRow* visit_row) override;
   VisitVector GetRedirectChain(VisitRow visit) override;
   bool GetForeignVisit(const std::string& originator_cache_guid,
diff --git a/components/history/core/browser/visit_database.cc b/components/history/core/browser/visit_database.cc
index 628de58..c6a77a5 100644
--- a/components/history/core/browser/visit_database.cc
+++ b/components/history/core/browser/visit_database.cc
@@ -909,15 +909,35 @@
   return FillVisitVectorWithOptions(statement, options, visits);
 }
 
-VisitID VisitDatabase::GetMostRecentVisitForURL(URLID url_id,
-                                                VisitRow* visit_row) {
+VisitID VisitDatabase::GetMostRecentVisitForURL(
+    URLID url_id,
+    VisitRow* visit_row,
+    VisitQuery404sPolicy policy_for_404_visits) {
   // The visit_time values can be duplicated in a redirect chain, so we sort
   // by id too, to ensure a consistent ordering just in case.
-  sql::Statement statement(GetDB().GetCachedStatement(
-      SQL_FROM_HERE, "SELECT" HISTORY_VISIT_ROW_FIELDS "FROM visits "
-                     "WHERE url=? "
-                     "ORDER BY visit_time DESC, id DESC "
-                     "LIMIT 1"));
+  sql::Statement statement;
+
+  switch (policy_for_404_visits) {
+    case VisitQuery404sPolicy::kInclude404s:
+      statement.Assign(GetDB().GetCachedStatement(
+          SQL_FROM_HERE, "SELECT" HISTORY_VISIT_ROW_FIELDS "FROM visits "
+                         "WHERE url=? "
+                         "ORDER BY visit_time DESC,id DESC "
+                         "LIMIT 1"));
+      break;
+    case VisitQuery404sPolicy::kExclude404s:
+      statement.Assign(GetDB().GetCachedStatement(
+          SQL_FROM_HERE, "SELECT" HISTORY_VISIT_ROW_FIELDS "FROM visits "
+                         "LEFT OUTER JOIN context_annotations "
+                         "ON visits.id=context_annotations.visit_id "
+                         "WHERE visits.url=?"
+                         "AND(context_annotations.response_code IS NULL "
+                         "OR context_annotations.response_code!=404) "
+                         "ORDER BY visits.visit_time DESC,visits.id DESC "
+                         "LIMIT 1"));
+      break;
+  }
+
   statement.BindInt64(0, url_id);
   if (!statement.Step())
     return 0;  // No visits for this URL.
diff --git a/components/history/core/browser/visit_database.h b/components/history/core/browser/visit_database.h
index a020373d..59271d0 100644
--- a/components/history/core/browser/visit_database.h
+++ b/components/history/core/browser/visit_database.h
@@ -175,11 +175,14 @@
                                VisitVector* visits);
 
   // Returns the visit ID for the most recent visit of the given URL ID, or 0
-  // if there is no visit for the URL.
+  // if there is no visit for the URL. Includes or excludes 404 visits according
+  // to `policy_for_404_visits`.
   //
   // If non-NULL, the given visit row will be filled with the information of
   // the found visit. When no visit is found, the row will be unchanged.
-  VisitID GetMostRecentVisitForURL(URLID url_id, VisitRow* visit_row);
+  VisitID GetMostRecentVisitForURL(URLID url_id,
+                                   VisitRow* visit_row,
+                                   VisitQuery404sPolicy policy_for_404_visits);
 
   // Returns the `max_results` most recent visit sessions for `url_id`. Includes
   // or excludes visits with an HTTP response code of 404 according to
diff --git a/components/history/core/browser/visit_database_unittest.cc b/components/history/core/browser/visit_database_unittest.cc
index f77ab21..a57d6d7c 100644
--- a/components/history/core/browser/visit_database_unittest.cc
+++ b/components/history/core/browser/visit_database_unittest.cc
@@ -11,6 +11,7 @@
 
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
+#include "components/history/core/browser/history_types.h"
 #include "components/history/core/browser/url_database.h"
 #include "components/history/core/browser/visit_annotations_database.h"
 #include "components/history/core/browser/visited_link_database.h"
@@ -241,7 +242,9 @@
 
   // Should return 0 when there are no visits.
   VisitRow out_visit;
-  EXPECT_EQ(GetMostRecentVisitForURL(kUrlId, &out_visit), 0U);
+  EXPECT_EQ(GetMostRecentVisitForURL(kUrlId, &out_visit,
+                                     VisitQuery404sPolicy::kInclude404s),
+            0U);
   EXPECT_EQ(out_visit.visit_id, 0U);
 }
 
@@ -261,7 +264,9 @@
 
   // The more recent visit should be returned.
   VisitRow out_visit;
-  EXPECT_EQ(GetMostRecentVisitForURL(kUrlId, &out_visit), 1U);
+  EXPECT_EQ(GetMostRecentVisitForURL(kUrlId, &out_visit,
+                                     VisitQuery404sPolicy::kInclude404s),
+            1U);
   EXPECT_EQ(out_visit.visit_time, kNow - base::Days(1));
 }
 
@@ -283,7 +288,9 @@
   // ID among the tied visits to be returned consistently. (These expectations
   // will flake if the tiebreaker isn't consistent.)
   VisitRow out_visit;
-  EXPECT_EQ(GetMostRecentVisitForURL(kUrlId, &out_visit), 2U);
+  EXPECT_EQ(GetMostRecentVisitForURL(kUrlId, &out_visit,
+                                     VisitQuery404sPolicy::kInclude404s),
+            2U);
   EXPECT_EQ(out_visit.visit_time, kNow);
 }
 
@@ -300,6 +307,42 @@
   EXPECT_EQ(out_visits.size(), 0U);
 }
 
+TEST_F(VisitDatabaseTest, GetMostRecentVisitForURL_404Policy) {
+  const URLID kUrlId = 1U;
+  const base::Time kNow = Time::Now();
+  VisitContextAnnotations context_annotations_non_404;
+  context_annotations_non_404.on_visit = {.response_code = 500};
+  VisitContextAnnotations context_annotations_404;
+  context_annotations_404.on_visit = {.response_code = 404};
+
+  // Add a non-404 visit for the URL.
+  VisitRow visit;
+  visit.url_id = kUrlId;
+  visit.visit_id = 1;
+  visit.visit_time = kNow - base::Days(2);
+  ASSERT_TRUE(AddVisit(&visit, SOURCE_BROWSED));
+  ASSERT_EQ(1, visit.visit_id);
+
+  // Add a visit with a 404 response code for the URL.
+  VisitRow visit_404;
+  visit_404.url_id = kUrlId;
+  visit_404.visit_id = 2;
+  visit_404.visit_time = kNow - base::Days(1);
+  ASSERT_TRUE(AddVisit(&visit_404, SOURCE_BROWSED));
+  AddContextAnnotationsForVisit(visit_404.visit_id, context_annotations_404);
+
+  // When including 404s, the 404 visit should be returned as the recent visit.
+  VisitRow out_visit;
+  EXPECT_EQ(GetMostRecentVisitForURL(kUrlId, &out_visit,
+                                     VisitQuery404sPolicy::kInclude404s),
+            2U);
+  EXPECT_EQ(out_visit.visit_time, kNow - base::Days(1));
+  EXPECT_EQ(GetMostRecentVisitForURL(kUrlId, &out_visit,
+                                     VisitQuery404sPolicy::kExclude404s),
+            1U);
+  EXPECT_EQ(out_visit.visit_time, kNow - base::Days(2));
+}
+
 TEST_F(VisitDatabaseTest, GetMostRecentVisitsForURL_Simple) {
   const URLID kUrlId = 1U;
   const base::Time kNow = Time::Now();
diff --git a/components/input/features.h b/components/input/features.h
index 78f89017..d652f7d4 100644
--- a/components/input/features.h
+++ b/components/input/features.h
@@ -54,6 +54,9 @@
 //    events and "looks ahead" at the next event to improve prediction.
 // 2. It generates a synthetic scroll event if the queue is empty, keeping
 //    scrolling smooth even if input events are missed.
+// This feature depends on kRefactorCompositorThreadEventQueue being enabled
+// to function correctly, as the refactored event queue logic is necessary
+// for the new predictor input mapping and synthetic event generation.
 COMPONENT_EXPORT(INPUT)
 BASE_DECLARE_FEATURE(kUpdateScrollPredictorInputMapping);
 COMPONENT_EXPORT(INPUT)
diff --git a/components/language_detection/core/browser/language_detection_model_service.cc b/components/language_detection/core/browser/language_detection_model_service.cc
index 6ba5a74..43700a8 100644
--- a/components/language_detection/core/browser/language_detection_model_service.cc
+++ b/components/language_detection/core/browser/language_detection_model_service.cc
@@ -17,7 +17,7 @@
       opt_guide_(opt_guide) {
   opt_guide_->AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
-      /*model_metadata=*/std::nullopt, this);
+      /*model_metadata=*/std::nullopt, background_task_runner, this);
 }
 
 LanguageDetectionModelService::~LanguageDetectionModelService() {
diff --git a/components/metrics/dwa/BUILD.gn b/components/metrics/dwa/BUILD.gn
index 0732560..d298d8c 100644
--- a/components/metrics/dwa/BUILD.gn
+++ b/components/metrics/dwa/BUILD.gn
@@ -1,7 +1,7 @@
 # Copyright 2024 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-import("//tools/metrics/private_metrics/gen_builders.gni")
+import("//tools/metrics/private_metrics/gen_private_metrics_builders.gni")
 
 private_metrics_builders("dwa_builders") {
   type = "dwa"
diff --git a/components/omnibox/browser/aim_eligibility_service.cc b/components/omnibox/browser/aim_eligibility_service.cc
index 7f5380f..aaf7e3d 100644
--- a/components/omnibox/browser/aim_eligibility_service.cc
+++ b/components/omnibox/browser/aim_eligibility_service.cc
@@ -501,6 +501,10 @@
   base::UmaHistogramBoolean(
       base::StrCat({sliced_prefix, ".is_pdf_upload_eligible"}),
       most_recent_response_.is_pdf_upload_eligible());
+  base::UmaHistogramSparse(base::StrCat({prefix, ".session_index"}),
+                           most_recent_response_.session_index());
+  base::UmaHistogramSparse(base::StrCat({sliced_prefix, ".session_index"}),
+                           most_recent_response_.session_index());
 }
 
 void AimEligibilityService::LogEligibilityResponseChange() const {
@@ -518,4 +522,7 @@
   base::UmaHistogramBoolean(base::StrCat({prefix, ".is_pdf_upload_eligible"}),
                             most_recent_response_.is_pdf_upload_eligible() !=
                                 prefs_response.is_pdf_upload_eligible());
+  base::UmaHistogramBoolean(
+      base::StrCat({prefix, ".session_index"}),
+      most_recent_response_.session_index() != prefs_response.session_index());
 }
diff --git a/components/omnibox/browser/autocomplete_scoring_model_executor_unittest.cc b/components/omnibox/browser/autocomplete_scoring_model_executor_unittest.cc
index 2157fe65..d24cafde 100644
--- a/components/omnibox/browser/autocomplete_scoring_model_executor_unittest.cc
+++ b/components/omnibox/browser/autocomplete_scoring_model_executor_unittest.cc
@@ -37,18 +37,20 @@
                            .AppendASCII("data")
                            .AppendASCII("omnibox")
                            .AppendASCII("adder.tflite");
-    execution_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
+    task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
         {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
     model_executor_ = std::make_unique<AutocompleteScoringModelExecutor>();
     model_executor_->InitializeAndMoveToExecutionThread(
         /*model_inference_timeout=*/std::nullopt,
         optimization_guide::proto::OPTIMIZATION_TARGET_OMNIBOX_URL_SCORING,
-        execution_task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
+        /*model_loading_task_runner=*/task_runner_,
+        /*execution_task_runner=*/task_runner_,
+        base::SequencedTaskRunner::GetCurrentDefault());
   }
 
   void TearDown() override {
     // Destroy model executor.
-    execution_task_runner_->DeleteSoon(FROM_HERE, std::move(model_executor_));
+    task_runner_->DeleteSoon(FROM_HERE, std::move(model_executor_));
     RunUntilIdle();
   }
 
@@ -57,13 +59,13 @@
  protected:
   base::test::TaskEnvironment task_environment_;
   base::FilePath model_file_path_;
-  scoped_refptr<base::SequencedTaskRunner> execution_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
   std::unique_ptr<AutocompleteScoringModelExecutor> model_executor_;
 };
 
 TEST_F(AutocompleteScoringModelExecutorTest, ExecuteModel) {
   // Update model file.
-  execution_task_runner_->PostTask(
+  task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
           &optimization_guide::ModelExecutor<ModelOutput,
@@ -84,7 +86,7 @@
           run_loop.get());
   base::TimeTicks now = base::TimeTicks::Now();
   ModelInput input = {1, 2};
-  execution_task_runner_->PostTask(
+  task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&optimization_guide::ModelExecutor<
                                     ModelOutput, ModelInput>::SendForExecution,
                                 model_executor_->GetWeakPtrForExecutionThread(),
diff --git a/components/omnibox/browser/autocomplete_scoring_model_handler.cc b/components/omnibox/browser/autocomplete_scoring_model_handler.cc
index 6609fdb..5659684 100644
--- a/components/omnibox/browser/autocomplete_scoring_model_handler.cc
+++ b/components/omnibox/browser/autocomplete_scoring_model_handler.cc
@@ -61,14 +61,16 @@
     scoped_refptr<base::SequencedTaskRunner> model_executor_task_runner,
     std::unique_ptr<AutocompleteScoringModelExecutor> model_executor,
     OptimizationTarget optimization_target,
-    const std::optional<optimization_guide::proto::Any>& model_metadata)
+    const std::optional<optimization_guide::proto::Any>& model_metadata,
+    scoped_refptr<base::SequencedTaskRunner> model_loading_task_runner)
     : optimization_guide::ModelHandler<ModelOutput, ModelInput>(
           model_provider,
           model_executor_task_runner,
           std::move(model_executor),
           /*model_inference_timeout=*/std::nullopt,
           optimization_target,
-          model_metadata) {
+          model_metadata,
+          model_loading_task_runner) {
   // Store the model in memory as soon as it is available and keep it loaded for
   // the whole browser session since model inference is latency sensitive and it
   // cannot wait for the model to be loaded from disk.
diff --git a/components/omnibox/browser/autocomplete_scoring_model_handler.h b/components/omnibox/browser/autocomplete_scoring_model_handler.h
index f5ea7b9..4c74207 100644
--- a/components/omnibox/browser/autocomplete_scoring_model_handler.h
+++ b/components/omnibox/browser/autocomplete_scoring_model_handler.h
@@ -30,7 +30,11 @@
       scoped_refptr<base::SequencedTaskRunner> model_executor_task_runner,
       std::unique_ptr<AutocompleteScoringModelExecutor> model_executor,
       optimization_guide::proto::OptimizationTarget optimization_target,
-      const std::optional<optimization_guide::proto::Any>& model_metadata);
+      const std::optional<optimization_guide::proto::Any>& model_metadata,
+      // If model_loading_task_runner is nullptr then model_task_runner will be
+      // used for model loading.
+      scoped_refptr<base::SequencedTaskRunner> model_loading_task_runner =
+          nullptr);
   ~AutocompleteScoringModelHandler() override;
 
   // Disallow copy/assign.
diff --git a/components/omnibox/browser/autocomplete_scoring_model_handler_unittest.cc b/components/omnibox/browser/autocomplete_scoring_model_handler_unittest.cc
index fc59162..93b19fd 100644
--- a/components/omnibox/browser/autocomplete_scoring_model_handler_unittest.cc
+++ b/components/omnibox/browser/autocomplete_scoring_model_handler_unittest.cc
@@ -60,6 +60,7 @@
       std::optional<base::TimeDelta>,
       optimization_guide::proto::OptimizationTarget,
       scoped_refptr<base::SequencedTaskRunner>,
+      scoped_refptr<base::SequencedTaskRunner>,
       scoped_refptr<base::SequencedTaskRunner>) override {}
 
   void UpdateModelFile(base::optional_ref<const base::FilePath>) override {}
diff --git a/components/omnibox/browser/autocomplete_scoring_model_service.cc b/components/omnibox/browser/autocomplete_scoring_model_service.cc
index f1d05e4..b3e1e47c 100644
--- a/components/omnibox/browser/autocomplete_scoring_model_service.cc
+++ b/components/omnibox/browser/autocomplete_scoring_model_service.cc
@@ -42,21 +42,21 @@
     : score_cache_(OmniboxFieldTrial::GetMLConfig().max_ml_score_cache_size) {
   // `model_provider` may be null for tests.
   if (OmniboxFieldTrial::IsUrlScoringModelEnabled() && model_provider) {
-    model_executor_task_runner_ =
-        base::SequencedTaskRunner::GetCurrentDefault();
-
     optimization_guide::proto::Any any_metadata;
     any_metadata.set_type_url(kAutocompleteScoringModelMetadataTypeUrl);
     optimization_guide::proto::AutocompleteScoringModelMetadata model_metadata;
     model_metadata.set_version(kAutocompleteScoringModelVersion);
     model_metadata.SerializeToString(any_metadata.mutable_value());
 
-    url_scoring_model_handler_ =
-        std::make_unique<AutocompleteScoringModelHandler>(
-            model_provider, model_executor_task_runner_.get(),
-            std::make_unique<AutocompleteScoringModelExecutor>(),
-            optimization_guide::proto::OPTIMIZATION_TARGET_OMNIBOX_URL_SCORING,
-            /*model_metadata=*/any_metadata);
+    url_scoring_model_handler_ = std::make_unique<
+        AutocompleteScoringModelHandler>(
+        model_provider,
+        /*model_task_runner=*/base::SequencedTaskRunner::GetCurrentDefault(),
+        std::make_unique<AutocompleteScoringModelExecutor>(),
+        optimization_guide::proto::OPTIMIZATION_TARGET_OMNIBOX_URL_SCORING,
+        /*model_metadata=*/any_metadata,
+        /*model_loading_task_runner=*/
+        base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}));
   }
 }
 
diff --git a/components/omnibox/browser/autocomplete_scoring_model_service.h b/components/omnibox/browser/autocomplete_scoring_model_service.h
index 698eeba6..66cd147 100644
--- a/components/omnibox/browser/autocomplete_scoring_model_service.h
+++ b/components/omnibox/browser/autocomplete_scoring_model_service.h
@@ -55,8 +55,6 @@
   // Returns whether the scoring model is enabled and loaded.
   bool UrlScoringModelAvailable();
 
-  scoped_refptr<base::SequencedTaskRunner> model_executor_task_runner_;
-
   // Autocomplete URL scoring model.
   std::unique_ptr<AutocompleteScoringModelHandler> url_scoring_model_handler_;
 
diff --git a/components/omnibox/browser/fake_on_device_tail_model_service.cc b/components/omnibox/browser/fake_on_device_tail_model_service.cc
index c2b1a15..70bf9b8 100644
--- a/components/omnibox/browser/fake_on_device_tail_model_service.cc
+++ b/components/omnibox/browser/fake_on_device_tail_model_service.cc
@@ -24,11 +24,11 @@
   // Only initialize the runner and the executor here, such that tests without
   // test task environment will not fail unless they explicitly call
   // `OnModelUpdated`.
-  model_executor_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
+  model_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
       {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
   tail_model_executor_ =
       ExecutorUniquePtr(new OnDeviceTailModelExecutor(),
-                        base::OnTaskRunnerDeleter(model_executor_task_runner_));
+                        base::OnTaskRunnerDeleter(model_task_runner_));
 
   OnDeviceTailModelService::OnModelUpdated(optimization_target, model_info);
 }
diff --git a/components/omnibox/browser/on_device_tail_model_service.cc b/components/omnibox/browser/on_device_tail_model_service.cc
index 875d660..b119e5ea9 100644
--- a/components/omnibox/browser/on_device_tail_model_service.cc
+++ b/components/omnibox/browser/on_device_tail_model_service.cc
@@ -104,11 +104,10 @@
 
 OnDeviceTailModelService::OnDeviceTailModelService(
     optimization_guide::OptimizationGuideModelProvider* model_provider)
-    : model_executor_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
+    : model_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT})),
-      tail_model_executor_(
-          new OnDeviceTailModelExecutor(),
-          base::OnTaskRunnerDeleter(model_executor_task_runner_)),
+      tail_model_executor_(new OnDeviceTailModelExecutor(),
+                           base::OnTaskRunnerDeleter(model_task_runner_)),
       model_provider_(model_provider) {
   if (model_provider_ == nullptr) {
     return;
@@ -117,7 +116,7 @@
   model_provider_->AddObserverForOptimizationTargetModel(
       optimization_guide::proto::
           OPTIMIZATION_TARGET_OMNIBOX_ON_DEVICE_TAIL_SUGGEST,
-      /* model_metadata= */ std::nullopt, this);
+      /* model_metadata= */ std::nullopt, model_task_runner_, this);
 
   memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
       FROM_HERE, base::MemoryPressureListenerTag::kOnDeviceTailModelService,
@@ -154,7 +153,7 @@
     return;
   }
   if (!model_info.has_value()) {
-    model_executor_task_runner_->PostTask(
+    model_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&OnDeviceTailModelExecutor::Reset,
                        base::Unretained(tail_model_executor_.get())));
@@ -175,7 +174,7 @@
     DVLOG(1) << "Failed to fetch metadata for Omnibox on device tail model";
     return;
   }
-  model_executor_task_runner_->PostTask(
+  model_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&InitializeTailModelExecutor, tail_model_executor_.get(),
                      model_info->GetModelFilePath(),
@@ -189,8 +188,8 @@
     return;
   }
 
-  if (model_executor_task_runner_) {
-    model_executor_task_runner_->PostTask(
+  if (model_task_runner_) {
+    model_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&MaybeUnloadModelExecutor, tail_model_executor_.get()));
   }
@@ -199,14 +198,14 @@
 void OnDeviceTailModelService::GetPredictionsForInput(
     const OnDeviceTailModelExecutor::ModelInput& input,
     ResultCallback result_callback) {
-  if (model_executor_task_runner_) {
+  if (model_task_runner_) {
     base::MemoryPressureMonitor* monitor = base::MemoryPressureMonitor::Get();
     // Do not call the model if memory pressure level is too high.
     if (!monitor ||
         monitor->GetCurrentPressureLevel(
             base::MemoryPressureMonitorTag::kOnDeviceTailModelService) !=
             base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
-      model_executor_task_runner_->PostTaskAndReplyWithResult(
+      model_task_runner_->PostTaskAndReplyWithResult(
           FROM_HERE,
           base::BindOnce(&RunTailModelExecutor, tail_model_executor_.get(),
                          input),
diff --git a/components/omnibox/browser/on_device_tail_model_service.h b/components/omnibox/browser/on_device_tail_model_service.h
index 733e8f09..2e61b56 100644
--- a/components/omnibox/browser/on_device_tail_model_service.h
+++ b/components/omnibox/browser/on_device_tail_model_service.h
@@ -58,9 +58,8 @@
   // demand.
   OnDeviceTailModelService();
 
-  // The task runner to run tail model executor.
-  scoped_refptr<base::SequencedTaskRunner> model_executor_task_runner_ =
-      nullptr;
+  // The task runner to run tail model.
+  scoped_refptr<base::SequencedTaskRunner> model_task_runner_ = nullptr;
 
   using ExecutorUniquePtr =
       std::unique_ptr<OnDeviceTailModelExecutor, base::OnTaskRunnerDeleter>;
diff --git a/components/one_time_tokens/android/backend/common/BUILD.gn b/components/one_time_tokens/android/backend/common/BUILD.gn
new file mode 100644
index 0000000..44762faa
--- /dev/null
+++ b/components/one_time_tokens/android/backend/common/BUILD.gn
@@ -0,0 +1,16 @@
+# Copyright 2025 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  sources = [
+    "java/src/org/chromium/components/one_time_tokens/backend/OneTimeTokensBackendErrorCode.java",
+    "java/src/org/chromium/components/one_time_tokens/backend/OneTimeTokensBackendException.java",
+  ]
+  deps = [
+    "//build/android:build_java",
+    "//third_party/androidx:androidx_annotation_annotation_java",
+  ]
+}
diff --git a/components/one_time_tokens/android/backend/common/java/src/org/chromium/components/one_time_tokens/backend/OneTimeTokensBackendErrorCode.java b/components/one_time_tokens/android/backend/common/java/src/org/chromium/components/one_time_tokens/backend/OneTimeTokensBackendErrorCode.java
new file mode 100644
index 0000000..d661d0b
--- /dev/null
+++ b/components/one_time_tokens/android/backend/common/java/src/org/chromium/components/one_time_tokens/backend/OneTimeTokensBackendErrorCode.java
@@ -0,0 +1,19 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.one_time_tokens.backend;
+
+import androidx.annotation.IntDef;
+
+import org.chromium.build.annotations.NullMarked;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@NullMarked
+@IntDef({OneTimeTokensBackendErrorCode.GMSCORE_VERSION_NOT_SUPPORTED})
+@Retention(RetentionPolicy.SOURCE)
+public @interface OneTimeTokensBackendErrorCode {
+    int GMSCORE_VERSION_NOT_SUPPORTED = 0;
+}
diff --git a/components/one_time_tokens/android/backend/common/java/src/org/chromium/components/one_time_tokens/backend/OneTimeTokensBackendException.java b/components/one_time_tokens/android/backend/common/java/src/org/chromium/components/one_time_tokens/backend/OneTimeTokensBackendException.java
new file mode 100644
index 0000000..0d23d8d
--- /dev/null
+++ b/components/one_time_tokens/android/backend/common/java/src/org/chromium/components/one_time_tokens/backend/OneTimeTokensBackendException.java
@@ -0,0 +1,25 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.one_time_tokens.backend;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.build.annotations.NullMarked;
+
+/** Exception thrown when there is an error in the one-time token backend. */
+@NullMarked
+public class OneTimeTokensBackendException extends Exception {
+    private final @OneTimeTokensBackendErrorCode int mErrorCode;
+
+    public OneTimeTokensBackendException(
+            @Nullable String message, @OneTimeTokensBackendErrorCode int errorCode) {
+        super(message);
+        mErrorCode = errorCode;
+    }
+
+    public @OneTimeTokensBackendErrorCode int getErrorCode() {
+        return mErrorCode;
+    }
+}
diff --git a/components/optimization_guide/content/browser/page_content_metadata_observer.cc b/components/optimization_guide/content/browser/page_content_metadata_observer.cc
index 615d0d7..4656967 100644
--- a/components/optimization_guide/content/browser/page_content_metadata_observer.cc
+++ b/components/optimization_guide/content/browser/page_content_metadata_observer.cc
@@ -87,6 +87,10 @@
   // and re-initialize them for the new page's frame tree.
   frame_data_.clear();
   UpdateFrameObservers();
+  // The meta tags are guaranteed to be empty here but we dispatch an update
+  // for all frames to ensure that observers are notified that any
+  // previous tags are no longer found (because of the navigation).
+  DispatchMetadata();
 }
 
 void PageContentMetadataObserver::UpdateFrameObservers() {
@@ -106,9 +110,16 @@
 
 void PageContentMetadataObserver::DispatchMetadata() {
   auto page_metadata = blink::mojom::PageMetadata::New();
-  for (const auto& it : frame_data_) {
-    if (it.second.metadata) {
-      page_metadata->frame_metadata.push_back(it.second.metadata->Clone());
+  for (const auto& [render_frame_host, frame_data] : frame_data_) {
+    if (frame_data.metadata) {
+      page_metadata->frame_metadata.push_back(frame_data.metadata->Clone());
+    } else {
+      // Create a representation for a frame with no matching meta tags.
+      auto frame_metadata = blink::mojom::FrameMetadata::New();
+      frame_metadata->url = GetURLForFrameMetadata(
+          render_frame_host->GetLastCommittedURL(),
+          render_frame_host->GetLastCommittedOrigin());
+      page_metadata->frame_metadata.push_back(std::move(frame_metadata));
     }
   }
   callback_.Run(std::move(page_metadata));
diff --git a/components/optimization_guide/content/browser/page_content_metadata_observer_browsertest.cc b/components/optimization_guide/content/browser/page_content_metadata_observer_browsertest.cc
index b11427e..1b8866f 100644
--- a/components/optimization_guide/content/browser/page_content_metadata_observer_browsertest.cc
+++ b/components/optimization_guide/content/browser/page_content_metadata_observer_browsertest.cc
@@ -276,8 +276,9 @@
   // The observer should be notified of the change.
   ASSERT_TRUE(callback_waiter_.Wait());
 
-  // The metadata should now be empty.
-  EXPECT_EQ(page_metadata()->frame_metadata.size(), 0u);
+  // The metadata should now contain one frame with no meta tags.
+  EXPECT_EQ(page_metadata()->frame_metadata.size(), 1u);
+  EXPECT_EQ(page_metadata()->frame_metadata[0]->meta_tags.size(), 0u);
 }
 
 IN_PROC_BROWSER_TEST_F(PageContentMetadataObserverBrowserTest,
diff --git a/components/optimization_guide/core/BUILD.gn b/components/optimization_guide/core/BUILD.gn
index d4966e6..c215eec 100644
--- a/components/optimization_guide/core/BUILD.gn
+++ b/components/optimization_guide/core/BUILD.gn
@@ -378,6 +378,7 @@
     "delivery/model_store_metadata_entry.h",
     "delivery/model_util.cc",
     "delivery/model_util.h",
+    "delivery/optimization_guide_model_provider.cc",
     "delivery/optimization_guide_model_provider.h",
     "delivery/optimization_target_model_observer.h",
     "delivery/prediction_manager.cc",
diff --git a/components/optimization_guide/core/delivery/model_provider_registry.cc b/components/optimization_guide/core/delivery/model_provider_registry.cc
index 1de6d6df..7add0c1 100644
--- a/components/optimization_guide/core/delivery/model_provider_registry.cc
+++ b/components/optimization_guide/core/delivery/model_provider_registry.cc
@@ -67,6 +67,7 @@
 void ModelProviderRegistry::AddObserverForOptimizationTargetModel(
     proto::OptimizationTarget optimization_target,
     const std::optional<proto::Any>& model_metadata,
+    scoped_refptr<base::SequencedTaskRunner> model_task_runner,
     OptimizationTargetModelObserver* observer) {
   CHECK(!model_metadata ||
         IsModelMetadataTypeOnServerAllowlist(*model_metadata));
diff --git a/components/optimization_guide/core/delivery/model_provider_registry.h b/components/optimization_guide/core/delivery/model_provider_registry.h
index b91e7db4..8a7ece7 100644
--- a/components/optimization_guide/core/delivery/model_provider_registry.h
+++ b/components/optimization_guide/core/delivery/model_provider_registry.h
@@ -26,6 +26,7 @@
   void AddObserverForOptimizationTargetModel(
       proto::OptimizationTarget optimization_target,
       const std::optional<proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       OptimizationTargetModelObserver* observer) override;
   void RemoveObserverForOptimizationTargetModel(
       proto::OptimizationTarget optimization_target,
diff --git a/components/optimization_guide/core/delivery/model_util.cc b/components/optimization_guide/core/delivery/model_util.cc
index 0efe3ee..6513ea0c 100644
--- a/components/optimization_guide/core/delivery/model_util.cc
+++ b/components/optimization_guide/core/delivery/model_util.cc
@@ -161,6 +161,10 @@
       return "PermissionsAiv4GeolocationDesktop";
     case proto::OPTIMIZATION_TARGET_PERMISSIONS_AIV4_NOTIFICATIONS_DESKTOP:
       return "PermissionsAiv4NotificationsDesktop";
+    case proto::OPTIMIZATION_TARGET_PERMISSIONS_AIV4_GEOLOCATION_ANDROID:
+      return "PermissionsAiv4GeolocationAndroid";
+    case proto::OPTIMIZATION_TARGET_PERMISSIONS_AIV4_NOTIFICATIONS_ANDROID:
+      return "PermissionsAiv4NotificationsAndroid";
     case proto::OPTIMIZATION_TARGET_GENERALIZED_SAFETY:
       return "GeneralizedSafety";
       // Whenever a new value is added, make sure to add it to the OptTarget
diff --git a/components/optimization_guide/core/delivery/optimization_guide_model_provider.cc b/components/optimization_guide/core/delivery/optimization_guide_model_provider.cc
new file mode 100644
index 0000000..efd6d22
--- /dev/null
+++ b/components/optimization_guide/core/delivery/optimization_guide_model_provider.cc
@@ -0,0 +1,43 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/optimization_guide/core/delivery/optimization_guide_model_provider.h"
+
+namespace optimization_guide {
+
+OptimizationGuideModelProviderObservation::
+    OptimizationGuideModelProviderObservation(
+        OptimizationGuideModelProvider* model_provider,
+        scoped_refptr<base::SequencedTaskRunner> model_task_runner,
+        OptimizationTargetModelObserver* observer)
+    : model_provider_(model_provider),
+      model_task_runner_(model_task_runner),
+      observer_(observer) {}
+
+OptimizationGuideModelProviderObservation::
+    ~OptimizationGuideModelProviderObservation() {
+  Reset();
+}
+
+void OptimizationGuideModelProviderObservation::Observe(
+    proto::OptimizationTarget optimization_target,
+    const std::optional<proto::Any>& model_metadata) {
+  optimization_target_ = optimization_target;
+  model_provider_->AddObserverForOptimizationTargetModel(
+      optimization_target, model_metadata, model_task_runner_, observer_);
+}
+
+void OptimizationGuideModelProviderObservation::Reset() {
+  if (optimization_target_ != proto::OPTIMIZATION_TARGET_UNKNOWN) {
+    model_provider_->RemoveObserverForOptimizationTargetModel(
+        optimization_target_, observer_);
+    optimization_target_ = proto::OPTIMIZATION_TARGET_UNKNOWN;
+  }
+}
+
+bool OptimizationGuideModelProviderObservation::IsRegistered() const {
+  return optimization_target_ != proto::OPTIMIZATION_TARGET_UNKNOWN;
+}
+
+}  // namespace optimization_guide
diff --git a/components/optimization_guide/core/delivery/optimization_guide_model_provider.h b/components/optimization_guide/core/delivery/optimization_guide_model_provider.h
index 8dd3fbe..0f1d4c11 100644
--- a/components/optimization_guide/core/delivery/optimization_guide_model_provider.h
+++ b/components/optimization_guide/core/delivery/optimization_guide_model_provider.h
@@ -7,6 +7,7 @@
 
 #include <optional>
 
+#include "base/task/sequenced_task_runner.h"
 #include "components/optimization_guide/core/delivery/optimization_target_model_observer.h"
 #include "components/optimization_guide/proto/models.pb.h"
 
@@ -16,7 +17,8 @@
 // for inference.
 class OptimizationGuideModelProvider {
  public:
-  // Adds an observer for updates to the model for |optimization_target|.
+  // Adds an observer for updates to the model for `optimization_target`.
+  // Model loading tasks are expected to be performed on `model_task_runner`.
   //
   // It is assumed that any model retrieved this way will be passed to the
   // Machine Learning Service for inference.
@@ -27,6 +29,7 @@
   virtual void AddObserverForOptimizationTargetModel(
       proto::OptimizationTarget optimization_target,
       const std::optional<proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       OptimizationTargetModelObserver* observer) = 0;
 
   // Removes an observer for updates to the model for |optimization_target|.
@@ -50,29 +53,17 @@
  public:
   OptimizationGuideModelProviderObservation(
       OptimizationGuideModelProvider* model_provider,
-      OptimizationTargetModelObserver* observer)
-      : model_provider_(model_provider), observer_(observer) {}
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
+      OptimizationTargetModelObserver* observer);
 
-  ~OptimizationGuideModelProviderObservation() { Reset(); }
+  ~OptimizationGuideModelProviderObservation();
 
   void Observe(proto::OptimizationTarget optimization_target,
-               const std::optional<proto::Any>& model_metadata) {
-    optimization_target_ = optimization_target;
-    model_provider_->AddObserverForOptimizationTargetModel(
-        optimization_target, model_metadata, observer_);
-  }
+               const std::optional<proto::Any>& model_metadata);
 
-  void Reset() {
-    if (optimization_target_ != proto::OPTIMIZATION_TARGET_UNKNOWN) {
-      model_provider_->RemoveObserverForOptimizationTargetModel(
-          optimization_target_, observer_);
-      optimization_target_ = proto::OPTIMIZATION_TARGET_UNKNOWN;
-    }
-  }
+  void Reset();
 
-  bool IsRegistered() const {
-    return optimization_target_ != proto::OPTIMIZATION_TARGET_UNKNOWN;
-  }
+  bool IsRegistered() const;
 
   OptimizationGuideModelProviderObservation(
       const OptimizationGuideModelProviderObservation&) = delete;
@@ -81,6 +72,7 @@
 
  private:
   const raw_ptr<OptimizationGuideModelProvider> model_provider_;
+  const scoped_refptr<base::SequencedTaskRunner> model_task_runner_;
   const raw_ptr<OptimizationTargetModelObserver> observer_;
 
   // The optimization target for which the models are being observed. If this is
diff --git a/components/optimization_guide/core/delivery/prediction_manager.cc b/components/optimization_guide/core/delivery/prediction_manager.cc
index 62bbcb9..d57dcb2 100644
--- a/components/optimization_guide/core/delivery/prediction_manager.cc
+++ b/components/optimization_guide/core/delivery/prediction_manager.cc
@@ -24,6 +24,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
@@ -91,6 +92,8 @@
       url_loader_factory_(url_loader_factory),
       optimization_guide_logger_(optimization_guide_logger),
       unzipper_factory_(std::move(unzipper_factory)),
+      default_model_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
+          {base::MayBlock(), base::TaskPriority::BEST_EFFORT})),
       prediction_model_fetch_timer_(
           local_state,
           base::BindRepeating(
@@ -115,11 +118,19 @@
 void PredictionManager::AddObserverForOptimizationTargetModel(
     proto::OptimizationTarget optimization_target,
     const std::optional<proto::Any>& model_metadata,
+    scoped_refptr<base::SequencedTaskRunner> model_task_runner,
     OptimizationTargetModelObserver* observer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  registry_.AddObserverForOptimizationTargetModel(optimization_target,
-                                                  model_metadata, observer);
+  // Set the target's task runner if none is already present. This has the
+  // effect of using the first registered model_task_runner for all model
+  // execution, if more than one observer gets registered (e.g. in the case of
+  // multiple profiles using the model).
+  optimization_target_model_task_runner_.emplace(optimization_target,
+                                                 model_task_runner);
+
+  registry_.AddObserverForOptimizationTargetModel(
+      optimization_target, model_metadata, model_task_runner, observer);
   base::UmaHistogramMediumTimes(
       "OptimizationGuide.PredictionManager.RegistrationTimeSinceServiceInit." +
           GetStringNameForOptimizationTarget(optimization_target),
@@ -289,6 +300,15 @@
   prediction_model_fetch_timer_.SchedulePeriodicModelsFetch();
 }
 
+scoped_refptr<base::SequencedTaskRunner> PredictionManager::GetModelTaskRunner(
+    proto::OptimizationTarget optimization_target) {
+  const auto loc =
+      optimization_target_model_task_runner_.find(optimization_target);
+  return loc != optimization_target_model_task_runner_.end()
+             ? loc->second
+             : default_model_task_runner_;
+}
+
 void PredictionManager::OnModelsFetched(
     const std::vector<proto::ModelInfo> models_request_info,
     std::unique_ptr<proto::GetModelsResponse> get_models_response_data) {
@@ -438,6 +458,7 @@
     // Load the model from the store to see whether it is valid or not.
     prediction_model_store_->LoadModel(
         optimization_target, model_cache_key_,
+        GetModelTaskRunner(optimization_target),
         base::BindOnce(&PredictionManager::MaybeDownloadOrUpdatePredictionModel,
                        ui_weak_ptr_factory_.GetWeakPtr(), optimization_target,
                        model));
@@ -619,6 +640,7 @@
     }
     prediction_model_store_->LoadModel(
         optimization_target, model_cache_key_,
+        GetModelTaskRunner(optimization_target),
         base::BindOnce(&PredictionManager::OnLoadPredictionModel,
                        ui_weak_ptr_factory_.GetWeakPtr(), optimization_target,
                        /*record_availability_metrics=*/true));
diff --git a/components/optimization_guide/core/delivery/prediction_manager.h b/components/optimization_guide/core/delivery/prediction_manager.h
index 9705440..3dc3b83 100644
--- a/components/optimization_guide/core/delivery/prediction_manager.h
+++ b/components/optimization_guide/core/delivery/prediction_manager.h
@@ -133,6 +133,7 @@
   void AddObserverForOptimizationTargetModel(
       proto::OptimizationTarget optimization_target,
       const std::optional<proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       OptimizationTargetModelObserver* observer) override;
   void RemoveObserverForOptimizationTargetModel(
       proto::OptimizationTarget optimization_target,
@@ -168,6 +169,10 @@
       const std::vector<proto::ModelInfo> models_request_info,
       std::unique_ptr<proto::GetModelsResponse> get_models_response_data);
 
+  // Gets the model task runner to use for the target.
+  scoped_refptr<base::SequencedTaskRunner> GetModelTaskRunner(
+      proto::OptimizationTarget optimization_target);
+
   // Load models for every target in |optimization_targets| that have not yet
   // been loaded from the store.
   void LoadPredictionModels(
@@ -296,6 +301,15 @@
   // Callback to build Unzipper remotes.
   unzip::UnzipperFactory unzipper_factory_;
 
+  // The task runner to use if AddObserverForOptimizationTargetModel was never
+  // invoked to provide one.
+  const scoped_refptr<base::SequencedTaskRunner> default_model_task_runner_;
+
+  // The task runner on which to run model loading.
+  base::flat_map<proto::OptimizationTarget,
+                 scoped_refptr<base::SequencedTaskRunner>>
+      optimization_target_model_task_runner_;
+
   // Time the prediction manager got initialized.
   // TODO(crbug.com/40861855): Remove this old model store once the new model
   // store is launched.
diff --git a/components/optimization_guide/core/delivery/prediction_manager_unittest.cc b/components/optimization_guide/core/delivery/prediction_manager_unittest.cc
index ccc51cd..a1690d1 100644
--- a/components/optimization_guide/core/delivery/prediction_manager_unittest.cc
+++ b/components/optimization_guide/core/delivery/prediction_manager_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/task/thread_pool.h"
 #include "base/test/gtest_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
@@ -381,6 +382,9 @@
     test_download_service_tracker_ =
         std::make_unique<TestProfileDownloadServiceTracker>();
 
+    task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
+        {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
+
     CreateAndInitializePredictionModelStore();
     RunUntilIdle();
   }
@@ -470,6 +474,10 @@
                                    should_fetch_model);
   }
 
+  scoped_refptr<base::SequencedTaskRunner> task_runner() {
+    return task_runner_;
+  }
+
  private:
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::MainThreadType::UI,
@@ -482,6 +490,7 @@
   std::unique_ptr<TestPredictionManager> prediction_manager_;
   std::unique_ptr<TestProfileDownloadServiceTracker>
       test_download_service_tracker_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
 };
 
 class PredictionManagerTest : public PredictionManagerTestBase {
@@ -533,7 +542,8 @@
 
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, task_runner(),
+      &observer);
   SetStoreInitialized();
 
   EXPECT_FALSE(prediction_model_fetcher()->models_fetched());
@@ -548,7 +558,8 @@
 
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, task_runner(),
+      &observer);
   SetStoreInitialized();
 
   EXPECT_TRUE(prediction_model_fetcher()->models_fetched());
@@ -572,7 +583,8 @@
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &observer);
   MoveClockForwardBy(base::Seconds(kUpdateFetchModelAndFeaturesTimeSecs));
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, task_runner(),
+      &observer);
   RunUntilIdle();
   EXPECT_FALSE(prediction_model_fetcher()->models_fetched());
   EXPECT_TRUE(observer.last_received_model_for_target(
@@ -603,7 +615,8 @@
 
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, model_metadata, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, model_metadata,
+      task_runner(), &observer);
   SetStoreInitialized();
   base::RunLoop().RunUntilIdle();
 
@@ -717,7 +730,7 @@
   FakeOptimizationTargetModelObserver observer1;
   prediction_manager()->AddObserverForOptimizationTargetModel(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
-      /*model_metadata=*/std::nullopt, &observer1);
+      /*model_metadata=*/std::nullopt, task_runner(), &observer1);
   SetStoreInitialized();
 
   // Ensure observer is hooked up.
@@ -739,7 +752,7 @@
   FakeOptimizationTargetModelObserver observer2;
   prediction_manager()->AddObserverForOptimizationTargetModel(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
-      /*model_metadata=*/std::nullopt, &observer2);
+      /*model_metadata=*/std::nullopt, task_runner(), &observer2);
   RunUntilIdle();
   EXPECT_EQ(base_model_dir1.Append(GetBaseFileNameForModels()),
             observer2
@@ -806,7 +819,8 @@
 
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, model_metadata, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, model_metadata,
+      task_runner(), &observer);
   SetStoreInitialized();
 
   // Make sure no models are fetched.
@@ -858,7 +872,8 @@
   SetStoreInitialized();
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_MODEL_VALIDATION, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_MODEL_VALIDATION, std::nullopt, task_runner(),
+      &observer);
 
   histogram_tester.ExpectUniqueSample(
       "OptimizationGuide.PredictionManager.ModelAvailableAtRegistration."
@@ -876,7 +891,8 @@
 
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, task_runner(),
+      &observer);
   SetStoreInitialized();
 
   EXPECT_TRUE(prediction_model_fetcher()->models_fetched());
@@ -897,7 +913,8 @@
   prediction_manager()->RemoveObserverForOptimizationTargetModel(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &observer);
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, task_runner(),
+      &observer);
   EXPECT_TRUE(observer.last_received_model_for_target(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
   // Model is delivered from store. No fetch happens.
@@ -912,7 +929,7 @@
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
-      /*model_metadata=*/std::nullopt, &observer);
+      /*model_metadata=*/std::nullopt, task_runner(), &observer);
 
   // Set invalid model with no download url.
   proto::PredictionModel model = CreatePredictionModelForModelStore(
@@ -939,7 +956,7 @@
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
-      /*model_metadata=*/std::nullopt, &observer);
+      /*model_metadata=*/std::nullopt, task_runner(), &observer);
 
   auto base_model_dir =
       GetBaseModelDir(proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
@@ -977,7 +994,8 @@
 
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, task_runner(),
+      &observer);
 
   SetStoreInitialized();
   EXPECT_FALSE(prediction_model_fetcher()->models_fetched());
@@ -1001,7 +1019,8 @@
 
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, task_runner(),
+      &observer);
 
   SetStoreInitialized();
   EXPECT_TRUE(prediction_model_fetcher()->models_fetched());
@@ -1042,7 +1061,8 @@
   // Now register the model.
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, task_runner(),
+      &observer);
 
   RunUntilIdle();
 
@@ -1080,7 +1100,8 @@
           PredictionModelFetcherEndState::kFetchFailed));
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, task_runner(),
+      &observer);
 
   SetStoreInitialized();
   EXPECT_FALSE(prediction_model_fetcher()->models_fetched());
@@ -1113,7 +1134,8 @@
 
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, task_runner(),
+      &observer);
   RunUntilIdle();
 
   EXPECT_FALSE(prediction_model_fetcher()->models_fetched());
@@ -1133,7 +1155,8 @@
 
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, task_runner(),
+      &observer);
 
   SetStoreInitialized();
   EXPECT_FALSE(prediction_model_fetcher()->models_fetched());
@@ -1157,7 +1180,8 @@
 
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, task_runner(),
+      &observer);
 
   SetStoreInitialized();
   EXPECT_TRUE(prediction_model_fetcher()->models_fetched());
@@ -1183,7 +1207,8 @@
   prediction_model_fetcher()->AddOptimizationTargetToSuccessFetch(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
   prediction_manager()->AddObserverForOptimizationTargetModel(
-      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, &observer);
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, std::nullopt, task_runner(),
+      &observer);
 
   // Load the model and let it be saved in the store.
   SetStoreInitialized();
diff --git a/components/optimization_guide/core/delivery/prediction_model_store.cc b/components/optimization_guide/core/delivery/prediction_model_store.cc
index a51043797f..a3a0576 100644
--- a/components/optimization_guide/core/delivery/prediction_model_store.cc
+++ b/components/optimization_guide/core/delivery/prediction_model_store.cc
@@ -237,6 +237,7 @@
 void PredictionModelStore::LoadModel(
     proto::OptimizationTarget optimization_target,
     const proto::ModelCacheKey& model_cache_key,
+    scoped_refptr<base::SequencedTaskRunner> model_task_runner,
     PredictionModelLoadedCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -265,11 +266,11 @@
     return;
   }
 
-  background_task_runner_->PostTaskAndReplyWithResult(
+  model_task_runner->PostTaskAndReplyWithResult(
       FROM_HERE,
-      base::BindOnce(
-          &PredictionModelStore::LoadAndVerifyModelInBackgroundThread,
-          optimization_target, base_store_dir_.Append(*base_model_dir)),
+      base::BindOnce(&PredictionModelStore::LoadAndVerifyModelOffThread,
+                     optimization_target,
+                     base_store_dir_.Append(*base_model_dir)),
       base::BindOnce(&PredictionModelStore::OnModelLoaded,
                      weak_ptr_factory_.GetWeakPtr(), optimization_target,
                      model_cache_key, std::move(callback)));
@@ -277,12 +278,11 @@
 
 // static
 std::unique_ptr<proto::PredictionModel>
-PredictionModelStore::LoadAndVerifyModelInBackgroundThread(
+PredictionModelStore::LoadAndVerifyModelOffThread(
     proto::OptimizationTarget optimization_target,
     const base::FilePath& base_model_dir) {
   TRACE_EVENT("optimization_guide",
-              "PredictionModelStore::LoadAndVerifyModelInBackgroundThread",
-              "target",
+              "PredictionModelStore::LoadAndVerifyModelOffThread", "target",
               GetStringNameForOptimizationTarget(optimization_target));
 
   auto model_info = ParseModelInfoFromFile(
diff --git a/components/optimization_guide/core/delivery/prediction_model_store.h b/components/optimization_guide/core/delivery/prediction_model_store.h
index 91cb8d8..49eff84 100644
--- a/components/optimization_guide/core/delivery/prediction_model_store.h
+++ b/components/optimization_guide/core/delivery/prediction_model_store.h
@@ -64,6 +64,7 @@
   // is invoked. On any failures, callback is run with nullptr.
   void LoadModel(proto::OptimizationTarget optimization_target,
                  const proto::ModelCacheKey& model_cache_key,
+                 scoped_refptr<base::SequencedTaskRunner> model_task_runner,
                  PredictionModelLoadedCallback callback);
 
   // Update the model metadata for |model_info| if the model represented by
@@ -112,8 +113,7 @@
 
   // Loads the model and verifies if the model files exist and returns the
   // model. Otherwise nullptr is returned on any failures.
-  static std::unique_ptr<proto::PredictionModel>
-  LoadAndVerifyModelInBackgroundThread(
+  static std::unique_ptr<proto::PredictionModel> LoadAndVerifyModelOffThread(
       proto::OptimizationTarget optimization_target,
       const base::FilePath& base_model_dir);
 
diff --git a/components/optimization_guide/core/delivery/prediction_model_store_unittest.cc b/components/optimization_guide/core/delivery/prediction_model_store_unittest.cc
index 6aededae..3fee416 100644
--- a/components/optimization_guide/core/delivery/prediction_model_store_unittest.cc
+++ b/components/optimization_guide/core/delivery/prediction_model_store_unittest.cc
@@ -109,6 +109,8 @@
     std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
     prediction_model_store_->LoadModel(
         optimization_target, model_cache_key,
+        base::ThreadPool::CreateSequencedTaskRunner(
+            {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
         base::BindOnce(&PredictionModelStoreTest::OnPredictionModelLoaded,
                        base::Unretained(this), run_loop.get()));
     run_loop->Run();
diff --git a/components/optimization_guide/core/delivery/test_optimization_guide_model_provider.cc b/components/optimization_guide/core/delivery/test_optimization_guide_model_provider.cc
index 8325a00..77390dd3 100644
--- a/components/optimization_guide/core/delivery/test_optimization_guide_model_provider.cc
+++ b/components/optimization_guide/core/delivery/test_optimization_guide_model_provider.cc
@@ -14,6 +14,7 @@
 void TestOptimizationGuideModelProvider::AddObserverForOptimizationTargetModel(
     optimization_guide::proto::OptimizationTarget optimization_target,
     const std::optional<proto::Any>& model_metadata,
+    scoped_refptr<base::SequencedTaskRunner> model_task_runner,
     optimization_guide::OptimizationTargetModelObserver* observer) {}
 
 void TestOptimizationGuideModelProvider::
diff --git a/components/optimization_guide/core/delivery/test_optimization_guide_model_provider.h b/components/optimization_guide/core/delivery/test_optimization_guide_model_provider.h
index dac3f760..ab513ab 100644
--- a/components/optimization_guide/core/delivery/test_optimization_guide_model_provider.h
+++ b/components/optimization_guide/core/delivery/test_optimization_guide_model_provider.h
@@ -26,6 +26,7 @@
   void AddObserverForOptimizationTargetModel(
       proto::OptimizationTarget optimization_target,
       const std::optional<proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       OptimizationTargetModelObserver* observer) override;
   void RemoveObserverForOptimizationTargetModel(
       proto::OptimizationTarget optimization_target,
diff --git a/components/optimization_guide/core/inference/base_model_executor.h b/components/optimization_guide/core/inference/base_model_executor.h
index a48b6306..2290de1 100644
--- a/components/optimization_guide/core/inference/base_model_executor.h
+++ b/components/optimization_guide/core/inference/base_model_executor.h
@@ -38,13 +38,15 @@
   void InitializeAndMoveToExecutionThread(
       std::optional<base::TimeDelta> model_inference_timeout,
       proto::OptimizationTarget optimization_target,
+      scoped_refptr<base::SequencedTaskRunner> model_loading_task_runner,
       scoped_refptr<base::SequencedTaskRunner> execution_task_runner,
       scoped_refptr<base::SequencedTaskRunner> reply_task_runner) override {
     num_threads_ = features::OverrideNumThreadsForOptTarget(optimization_target)
                        .value_or(-1);
     TFLiteModelExecutor<OutputType, InputType>::
         InitializeAndMoveToExecutionThread(
-            model_inference_timeout, optimization_target, execution_task_runner,
+            model_inference_timeout, optimization_target,
+            model_loading_task_runner, execution_task_runner,
             reply_task_runner);
   }
 
diff --git a/components/optimization_guide/core/inference/model_executor.h b/components/optimization_guide/core/inference/model_executor.h
index 3787d52..806b608 100644
--- a/components/optimization_guide/core/inference/model_executor.h
+++ b/components/optimization_guide/core/inference/model_executor.h
@@ -44,6 +44,7 @@
   virtual void InitializeAndMoveToExecutionThread(
       std::optional<base::TimeDelta> model_inference_timeout,
       proto::OptimizationTarget optimization_target,
+      scoped_refptr<base::SequencedTaskRunner> model_loading_task_runner,
       scoped_refptr<base::SequencedTaskRunner> execution_task_runner,
       scoped_refptr<base::SequencedTaskRunner> reply_task_runner) = 0;
 
diff --git a/components/optimization_guide/core/inference/model_handler.h b/components/optimization_guide/core/inference/model_handler.h
index b40be92..ea74b02a 100644
--- a/components/optimization_guide/core/inference/model_handler.h
+++ b/components/optimization_guide/core/inference/model_handler.h
@@ -46,8 +46,8 @@
 // This class owns and handles the execution of models on the UI thread.
 // Derived classes must provide an implementation of `ModelExecutor`
 // which is then owned by `this`. The passed executor will be called
-// and destroyed on the thread specified by `model_executor_task_runner`,
-// which is all handled by this class.
+// and destroyed on the thread specified by `model_task_runner`, which
+// is all handled by this class.
 //
 // Derived classes that override `OnModelUpdated` must call the parent
 // `OnModelUpdated` as the first step, for the internal state to be updated.
@@ -56,18 +56,22 @@
  public:
   ModelHandler(
       OptimizationGuideModelProvider* model_provider,
-      scoped_refptr<base::SequencedTaskRunner> model_executor_task_runner,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       std::unique_ptr<ModelExecutor<OutputType, InputType>> model_executor,
       // Passing nullopt will use a default value.
       std::optional<base::TimeDelta> model_inference_timeout,
       proto::OptimizationTarget optimization_target,
       const std::optional<proto::Any>& model_metadata,
+      // If model_loading_task_runner is nullptr then model_task_runner will be
+      // used for model loading.
+      scoped_refptr<base::SequencedTaskRunner> model_loading_task_runner =
+          nullptr,
       scoped_refptr<base::SequencedTaskRunner> reply_task_runner =
           base::SequencedTaskRunner::GetCurrentDefault())
       : model_provider_(model_provider),
         optimization_target_(optimization_target),
         model_executor_(std::move(model_executor)),
-        model_executor_task_runner_(model_executor_task_runner) {
+        model_task_runner_(model_task_runner) {
     DCHECK(model_provider_);
     DCHECK(model_executor_);
     DCHECK_NE(optimization_target_,
@@ -83,14 +87,19 @@
 
     handler_created_time_ = base::TimeTicks::Now();
 
+    if (!model_loading_task_runner) {
+      model_loading_task_runner = model_task_runner;
+    }
+
     model_executor_->InitializeAndMoveToExecutionThread(
         model_inference_timeout, optimization_target_,
-        model_executor_task_runner_, reply_task_runner);
+        model_loading_task_runner, model_task_runner_,
+        /*reply_task_runner=*/base::SequencedTaskRunner::GetCurrentDefault());
 
     // Run this after the executor is initialized in case the model is already
     // available.
     model_provider_->AddObserverForOptimizationTargetModel(
-        optimization_target_, model_metadata, this);
+        optimization_target_, model_metadata, model_loading_task_runner, this);
   }
   ~ModelHandler() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -100,8 +109,7 @@
 
     // |model_executor_|'s  WeakPtrs are used on the model thread, so
     // that is also where the class must be destroyed.
-    model_executor_task_runner_->DeleteSoon(FROM_HERE,
-                                            std::move(model_executor_));
+    model_task_runner_->DeleteSoon(FROM_HERE, std::move(model_executor_));
   }
   ModelHandler(const ModelHandler&) = delete;
   ModelHandler& operator=(const ModelHandler&) = delete;
@@ -115,8 +123,8 @@
                                      InputType input) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-    model_executor_task_runner_->PostTask(
-        FROM_HERE, GetExecutionTask(std::move(callback), input));
+    model_task_runner_->PostTask(FROM_HERE,
+                                 GetExecutionTask(std::move(callback), input));
   }
 
   // Same as the method above. But also receives a `base::CancelableTaskTracker`
@@ -128,7 +136,7 @@
                                      InputType input) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-    tracker->PostTask(model_executor_task_runner_.get(), FROM_HERE,
+    tracker->PostTask(model_task_runner_.get(), FROM_HERE,
                       GetExecutionTask(std::move(callback), input));
   }
 
@@ -150,7 +158,7 @@
       BatchExecutionCallback callback,
       typename ModelExecutor<OutputType, InputType>::ConstRefInputVector
           batch_input) {
-    model_executor_task_runner_->PostTask(
+    model_task_runner_->PostTask(
         FROM_HERE, GetBatchExecutionTask(std::move(callback), batch_input));
   }
 
@@ -162,7 +170,7 @@
           batch_input) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-    tracker->PostTask(model_executor_task_runner_.get(), FROM_HERE,
+    tracker->PostTask(model_task_runner_.get(), FROM_HERE,
                       GetBatchExecutionTask(std::move(callback), batch_input));
   }
 
@@ -185,7 +193,7 @@
   // constructed TFLite graph.
   void SetShouldUnloadModelOnComplete(bool should_auto_unload) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    model_executor_task_runner_->PostTask(
+    model_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(
             &ModelExecutor<OutputType,
@@ -200,7 +208,7 @@
   // constructed TFLite graph.
   void SetShouldPreloadModel(bool should_preload_model) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    model_executor_task_runner_->PostTask(
+    model_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(
             &ModelExecutor<OutputType, InputType>::SetShouldPreloadModel,
@@ -213,7 +221,7 @@
   // signal.
   virtual void UnloadModel() {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    model_executor_task_runner_->PostTask(
+    model_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&ModelExecutor<OutputType, InputType>::UnloadModel,
                        model_executor_->GetWeakPtrForExecutionThread()));
@@ -248,7 +256,7 @@
       model_info_ = std::nullopt;
     }
 
-    model_executor_task_runner_->PostTask(
+    model_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&ModelExecutor<OutputType, InputType>::UpdateModelFile,
                        model_executor_->GetWeakPtrForExecutionThread(),
@@ -374,7 +382,7 @@
   // the task takes a reference to the TaskRunner (in a cyclic dependency) so
   // |base::Unretained| is not safe anywhere in this class or the
   // |model_executor_|.
-  scoped_refptr<base::SequencedTaskRunner> model_executor_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> model_task_runner_;
 
   // Set in |OnModelUpdated|.
   std::optional<ModelInfo> model_info_ GUARDED_BY_CONTEXT(sequence_checker_);
diff --git a/components/optimization_guide/core/inference/model_handler_unittest.cc b/components/optimization_guide/core/inference/model_handler_unittest.cc
index 4ce4b3c..1eed19a8 100644
--- a/components/optimization_guide/core/inference/model_handler_unittest.cc
+++ b/components/optimization_guide/core/inference/model_handler_unittest.cc
@@ -21,6 +21,7 @@
   void AddObserverForOptimizationTargetModel(
       proto::OptimizationTarget target,
       const std::optional<proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       OptimizationTargetModelObserver* observer) override {
     // Make sure we send what is expected based on
     // TestModelHandler ctor.
diff --git a/components/optimization_guide/core/inference/model_validator_unittest.cc b/components/optimization_guide/core/inference/model_validator_unittest.cc
index c12da83..c27cddf 100644
--- a/components/optimization_guide/core/inference/model_validator_unittest.cc
+++ b/components/optimization_guide/core/inference/model_validator_unittest.cc
@@ -47,6 +47,7 @@
   void AddObserverForOptimizationTargetModel(
       proto::OptimizationTarget optimization_target,
       const std::optional<proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       OptimizationTargetModelObserver* observer) override {
     if (optimization_target == proto::OPTIMIZATION_TARGET_MODEL_VALIDATION) {
       EXPECT_FALSE(model_validation_observer_);
diff --git a/components/optimization_guide/core/inference/test_model_executor.h b/components/optimization_guide/core/inference/test_model_executor.h
index c6c8353..43fc37a 100644
--- a/components/optimization_guide/core/inference/test_model_executor.h
+++ b/components/optimization_guide/core/inference/test_model_executor.h
@@ -20,6 +20,7 @@
       std::optional<base::TimeDelta>,
       proto::OptimizationTarget,
       scoped_refptr<base::SequencedTaskRunner>,
+      scoped_refptr<base::SequencedTaskRunner>,
       scoped_refptr<base::SequencedTaskRunner>) override {}
 
   void UpdateModelFile(base::optional_ref<const base::FilePath>) override {}
diff --git a/components/optimization_guide/core/inference/tflite_model_executor.h b/components/optimization_guide/core/inference/tflite_model_executor.h
index dac9be80..d8c239b 100644
--- a/components/optimization_guide/core/inference/tflite_model_executor.h
+++ b/components/optimization_guide/core/inference/tflite_model_executor.h
@@ -101,6 +101,7 @@
   void InitializeAndMoveToExecutionThread(
       std::optional<base::TimeDelta> model_inference_timeout,
       proto::OptimizationTarget optimization_target,
+      scoped_refptr<base::SequencedTaskRunner> model_loading_task_runner,
       scoped_refptr<base::SequencedTaskRunner> execution_task_runner,
       scoped_refptr<base::SequencedTaskRunner> reply_task_runner) override {
     DCHECK(!execution_task_runner_);
@@ -112,8 +113,7 @@
     optimization_target_ = optimization_target;
     execution_task_runner_ = execution_task_runner;
     reply_task_runner_ = reply_task_runner;
-    model_loading_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
-        {base::MayBlock(), GetModelLoadingTaskPriority()});
+    model_loading_task_runner_ = model_loading_task_runner;
 
     if (features::IsModelExecutionWatchdogEnabled()) {
       // The sequence |watchdog_sequence| is used to run watchdog's task. The
diff --git a/components/optimization_guide/core/inference/tflite_model_executor_unittest.cc b/components/optimization_guide/core/inference/tflite_model_executor_unittest.cc
index 584ba7c..8277a2d 100644
--- a/components/optimization_guide/core/inference/tflite_model_executor_unittest.cc
+++ b/components/optimization_guide/core/inference/tflite_model_executor_unittest.cc
@@ -893,7 +893,7 @@
 
   void CreateModelHandler() override {
     foreground_execution_sequence_ =
-        base::ThreadPool::CreateSequencedTaskRunner({});
+        base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
 
     model_handler_ = std::make_unique<TestTFLiteModelHandler>(
         test_model_provider(), foreground_execution_sequence_);
diff --git a/components/optimization_guide/core/model_execution/on_device_asset_manager.cc b/components/optimization_guide/core/model_execution/on_device_asset_manager.cc
index 23d41df..22146714 100644
--- a/components/optimization_guide/core/model_execution/on_device_asset_manager.cc
+++ b/components/optimization_guide/core/model_execution/on_device_asset_manager.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/task/thread_pool.h"
 #include "components/optimization_guide/core/delivery/optimization_guide_model_provider.h"
 #include "components/optimization_guide/core/model_execution/model_execution_features.h"
 #include "components/optimization_guide/core/model_execution/model_execution_util.h"
@@ -49,8 +50,16 @@
           base::BindRepeating(
               &OnDeviceModelServiceController::MaybeUpdateModelAdaptation,
               service_controller.GetWeakPtr())),
-      text_safety_model_observation_(&model_provider, this),
-      language_detection_model_observation_(&model_provider, this) {
+      text_safety_model_observation_(
+          &model_provider,
+          base::ThreadPool::CreateSequencedTaskRunner(
+              {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
+          this),
+      language_detection_model_observation_(
+          &model_provider,
+          base::ThreadPool::CreateSequencedTaskRunner(
+              {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
+          this) {
   usage_tracker_->AddObserver(this);
   on_device_component_state_manager_->AddObserver(this);
 
diff --git a/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.cc b/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.cc
index 1a60fdb..7e591a21 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.cc
@@ -194,10 +194,12 @@
     : feature_(feature),
       target_(
           *features::internal::GetOptimizationTargetForCapability(feature_)),
-      model_provider_observation_(&model_provider, this),
-      on_load_fn_(on_load_fn),
       background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
-          {base::MayBlock(), base::TaskPriority::BEST_EFFORT})) {}
+          {base::MayBlock(), base::TaskPriority::BEST_EFFORT})),
+      model_provider_observation_(&model_provider,
+                                  background_task_runner_,
+                                  this),
+      on_load_fn_(on_load_fn) {}
 
 OnDeviceModelAdaptationLoader::~OnDeviceModelAdaptationLoader() {
   Unregister();
diff --git a/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.h b/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.h
index 84708c5..a1816a1 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.h
+++ b/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.h
@@ -158,15 +158,15 @@
   ModelBasedCapabilityKey feature_;
   proto::OptimizationTarget target_;
 
+  // Background thread where file processing should be performed.
+  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+
   // The model provider to observe for updates to model adaptations.
   OptimizationGuideModelProviderObservation model_provider_observation_;
   OnLoadFn on_load_fn_;
 
   // The compatibility spec that we've registered for adaptations with.
   std::optional<OnDeviceBaseModelSpec> registered_spec_;
-
-  // Background thread where file processing should be performed.
-  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
 };
 
 class AdaptationLoaderMap final {
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index 20c4cc3..257e69c 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit 20c4cc39d64f47cd19ec46b550ec6cbbf0951ed7
+Subproject commit 257e69c2cd8457dd9d8d180c96295ad7ea650b1e
diff --git a/components/optimization_guide/proto/models.proto b/components/optimization_guide/proto/models.proto
index effb6b2..67bc559 100644
--- a/components/optimization_guide/proto/models.proto
+++ b/components/optimization_guide/proto/models.proto
@@ -263,6 +263,10 @@
   // Target for a newer, generalized safety model, designed to replace the
   // model associated with target `OPTIMIZATION_TARGET_TEXT_SAFETY`.
   OPTIMIZATION_TARGET_GENERALIZED_SAFETY = 67;
+  // Target for the text+image AIv4 geolocation permission feature model.
+  OPTIMIZATION_TARGET_PERMISSIONS_AIV4_GEOLOCATION_ANDROID = 68;
+  // Target for the text+image AIv4 notifications permission feature model.
+  OPTIMIZATION_TARGET_PERMISSIONS_AIV4_NOTIFICATIONS_ANDROID = 69;
 }
 
 // The model engine versions that can be used to do model inference.
diff --git a/components/origin_matcher/BUILD.gn b/components/origin_matcher/BUILD.gn
index 71ecb28..3e8b22c6 100644
--- a/components/origin_matcher/BUILD.gn
+++ b/components/origin_matcher/BUILD.gn
@@ -40,6 +40,8 @@
 source_set("origin_matcher") {
   public = [ "origin_matcher.h" ]
   sources = [
+    "features.cc",
+    "features.h",
     "origin_matcher.cc",
     "origin_matcher_internal.cc",
     "origin_matcher_internal.h",
diff --git a/components/origin_matcher/features.cc b/components/origin_matcher/features.cc
new file mode 100644
index 0000000..540cd2a
--- /dev/null
+++ b/components/origin_matcher/features.cc
@@ -0,0 +1,14 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/features.h"
+
+#include "components/origin_matcher/features.h"
+
+namespace origin_matcher {
+
+BASE_FEATURE(kOriginMatcherNewCopyAssignment,
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+}  // namespace origin_matcher
diff --git a/components/origin_matcher/features.h b/components/origin_matcher/features.h
new file mode 100644
index 0000000..878cb94
--- /dev/null
+++ b/components/origin_matcher/features.h
@@ -0,0 +1,18 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_ORIGIN_MATCHER_FEATURES_H_
+#define COMPONENTS_ORIGIN_MATCHER_FEATURES_H_
+
+#include "base/features.h"
+
+namespace origin_matcher {
+
+// When enabled, OriginMatcher::operator= will use the new, hopefully faster
+// implementation that does not serialize and deserialize all the rules.
+BASE_DECLARE_FEATURE(kOriginMatcherNewCopyAssignment);
+
+}  // namespace origin_matcher
+
+#endif  // COMPONENTS_ORIGIN_MATCHER_FEATURES_H_
diff --git a/components/origin_matcher/origin_matcher.cc b/components/origin_matcher/origin_matcher.cc
index b1ba0c76..e5b3d1d 100644
--- a/components/origin_matcher/origin_matcher.cc
+++ b/components/origin_matcher/origin_matcher.cc
@@ -5,7 +5,9 @@
 #include "components/origin_matcher/origin_matcher.h"
 
 #include "base/containers/adapters.h"
+#include "base/feature_list.h"
 #include "base/strings/string_util.h"
+#include "components/origin_matcher/features.h"
 #include "components/origin_matcher/origin_matcher_internal.h"
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
@@ -53,9 +55,18 @@
 
 OriginMatcher& OriginMatcher::operator=(const OriginMatcher& rhs) {
   rules_.clear();
-  for (const auto& rule : rhs.Serialize()) {
-    AddRuleFromString(rule);
+
+  if (base::FeatureList::IsEnabled(kOriginMatcherNewCopyAssignment)) {
+    rules_.reserve(rhs.rules_.size());
+    for (const auto& rule : rhs.rules_) {
+      rules_.push_back(rule->Clone());
+    }
+  } else {
+    for (const auto& rule : rhs.Serialize()) {
+      AddRuleFromString(rule);
+    }
   }
+
   return *this;
 }
 
diff --git a/components/origin_matcher/origin_matcher_internal.cc b/components/origin_matcher/origin_matcher_internal.cc
index d4accec..d7e8bd24 100644
--- a/components/origin_matcher/origin_matcher_internal.cc
+++ b/components/origin_matcher/origin_matcher_internal.cc
@@ -49,6 +49,10 @@
 
 MatchAllOriginsRule::~MatchAllOriginsRule() = default;
 
+std::unique_ptr<OriginMatcherRule> MatchAllOriginsRule::Clone() const {
+  return std::make_unique<MatchAllOriginsRule>();
+}
+
 net::SchemeHostPortMatcherResult MatchAllOriginsRule::Evaluate(
     const GURL& url) const {
   return net::SchemeHostPortMatcherResult::kInclude;
@@ -105,6 +109,11 @@
   return HostWildcardSanityCheck(host);
 }
 
+std::unique_ptr<OriginMatcherRule> SubdomainMatchingRule::Clone() const {
+  return std::make_unique<SubdomainMatchingRule>(scheme_, optional_host_,
+                                                 optional_port_);
+}
+
 net::SchemeHostPortMatcherResult SubdomainMatchingRule::Evaluate(
     const GURL& url) const {
   if (optional_port_ != -1 && url.EffectiveIntPort() != optional_port_) {
diff --git a/components/origin_matcher/origin_matcher_internal.h b/components/origin_matcher/origin_matcher_internal.h
index 0898acd..ebbe71f4 100644
--- a/components/origin_matcher/origin_matcher_internal.h
+++ b/components/origin_matcher/origin_matcher_internal.h
@@ -23,6 +23,7 @@
   ~OriginMatcherRule() override;
 
   OriginMatcherRuleType type() const { return type_; }
+  virtual std::unique_ptr<OriginMatcherRule> Clone() const = 0;
 
  private:
   const OriginMatcherRuleType type_;
@@ -37,6 +38,9 @@
   ~MatchAllOriginsRule() override;
 
   // OriginMatcherRule:
+  std::unique_ptr<OriginMatcherRule> Clone() const override;
+
+  // net::SchemeHostPortMatcherRule:
   net::SchemeHostPortMatcherResult Evaluate(const GURL& url) const override;
   std::string ToString() const override;
 };
@@ -73,6 +77,9 @@
   int optional_port() const { return optional_port_; }
 
   // OriginMatcherRule:
+  std::unique_ptr<OriginMatcherRule> Clone() const override;
+
+  // net::SchemeHostPortMatcherRule:
   net::SchemeHostPortMatcherResult Evaluate(const GURL& url) const override;
   std::string ToString() const override;
 
diff --git a/components/origin_matcher/origin_matcher_unittest.cc b/components/origin_matcher/origin_matcher_unittest.cc
index d2cf5915..321e1e6 100644
--- a/components/origin_matcher/origin_matcher_unittest.cc
+++ b/components/origin_matcher/origin_matcher_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "components/origin_matcher/origin_matcher.h"
 
+#include "base/test/scoped_feature_list.h"
+#include "components/origin_matcher/features.h"
 #include "components/origin_matcher/origin_matcher.mojom.h"
 #include "components/origin_matcher/origin_matcher_internal.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
@@ -402,4 +404,18 @@
   ASSERT_NO_FATAL_FAILURE(CompareMatchers(matcher, deserialized));
 }
 
+TEST_F(OriginMatcherTest, CopyAndAssign) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(kOriginMatcherNewCopyAssignment);
+
+  OriginMatcher matcher;
+  EXPECT_TRUE(matcher.AddRuleFromString("*"));
+  EXPECT_TRUE(matcher.AddRuleFromString("https://*.example.com"));
+
+  OriginMatcher assigned = matcher;
+  EXPECT_EQ(2u, assigned.rules().size());
+  EXPECT_EQ("*", assigned.rules()[0]->ToString());
+  EXPECT_EQ("https://*.example.com:443", assigned.rules()[1]->ToString());
+}
+
 }  // namespace origin_matcher
diff --git a/components/page_content_annotations/core/edu_classifier_model_handler_unittest.cc b/components/page_content_annotations/core/edu_classifier_model_handler_unittest.cc
index 1a050bd..0f2861f 100644
--- a/components/page_content_annotations/core/edu_classifier_model_handler_unittest.cc
+++ b/components/page_content_annotations/core/edu_classifier_model_handler_unittest.cc
@@ -25,6 +25,7 @@
   void AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OptimizationTarget optimization_target,
       const std::optional<optimization_guide::proto::Any>& any,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       optimization_guide::OptimizationTargetModelObserver* observer) override {
     if (optimization_target ==
         optimization_guide::proto::OPTIMIZATION_TARGET_EDU_CLASSIFIER) {
diff --git a/components/page_content_annotations/core/page_content_annotations_model_manager_unittest.cc b/components/page_content_annotations/core/page_content_annotations_model_manager_unittest.cc
index ad5c3db..4b0cbe6 100644
--- a/components/page_content_annotations/core/page_content_annotations_model_manager_unittest.cc
+++ b/components/page_content_annotations/core/page_content_annotations_model_manager_unittest.cc
@@ -32,6 +32,7 @@
   void AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OptimizationTarget target,
       const std::optional<optimization_guide::proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       optimization_guide::OptimizationTargetModelObserver* observer) override {
     registered_model_metadata_.insert_or_assign(target, model_metadata);
   }
diff --git a/components/page_content_annotations/core/page_visibility_model_fuzzer.cc b/components/page_content_annotations/core/page_visibility_model_fuzzer.cc
index e68603e8..d68e71d 100644
--- a/components/page_content_annotations/core/page_visibility_model_fuzzer.cc
+++ b/components/page_content_annotations/core/page_visibility_model_fuzzer.cc
@@ -20,6 +20,8 @@
         // timeout behavior here, libfuzzer will take care of hangs.
         /*model_inference_timeout=*/base::Minutes(60),
         optimization_guide::proto::OPTIMIZATION_TARGET_PAGE_VISIBILITY,
+        /*model_loading_task_runner=*/
+        task_environment_.GetMainThreadTaskRunner(),
         /*execution_task_runner=*/task_environment_.GetMainThreadTaskRunner(),
         /*reply_task_runner=*/task_environment_.GetMainThreadTaskRunner());
 
diff --git a/components/page_info/BUILD.gn b/components/page_info/BUILD.gn
index dc4f7d1..9951a53 100644
--- a/components/page_info/BUILD.gn
+++ b/components/page_info/BUILD.gn
@@ -26,6 +26,7 @@
     "//components/privacy_sandbox",
     "//components/safe_browsing:buildflags",
     "//components/safe_browsing/content/browser/password_protection",
+    "//components/safe_browsing/core/browser:safe_browsing_metrics_collector",
     "//components/safe_browsing/core/browser/password_protection:password_protection_metrics_util",
     "//components/safe_browsing/core/common",
     "//components/safe_browsing/core/common/proto:csd_proto",
diff --git a/components/page_info/android/java/res/layout/connection_security.xml b/components/page_info/android/java/res/layout/connection_security.xml
index aaf1a96..5d29ff0b 100644
--- a/components/page_info/android/java/res/layout/connection_security.xml
+++ b/components/page_info/android/java/res/layout/connection_security.xml
@@ -41,7 +41,6 @@
                 android:id="@+id/security_description_details"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:paddingTop="2dp"
                 android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
                 app:leading="@dimen/text_size_medium_leading"
                 android:clickable="true"
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/ConnectionSecurityView.java b/components/page_info/android/java/src/org/chromium/components/page_info/ConnectionSecurityView.java
index 1c1517f..d2db03c 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/ConnectionSecurityView.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/ConnectionSecurityView.java
@@ -13,6 +13,7 @@
 import android.text.SpannableStringBuilder;
 import android.text.style.ForegroundColorSpan;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -29,6 +30,7 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
+import org.chromium.ui.base.ViewUtils;
 import org.chromium.ui.widget.ChromeImageView;
 
 /**
@@ -45,18 +47,13 @@
     public static class ViewParams {
         public @DrawableRes int iconResId;
         public @ColorRes int iconTint;
-        public CharSequence summary;
-        public CharSequence details;
+        public @Nullable CharSequence summary;
+        public @Nullable CharSequence details;
         public @Nullable Runnable resetDecisionsCallback;
         public byte @Nullable [][] certChain;
         public boolean isCert1Qwac;
         public byte @Nullable [][] twoQwacCertChain;
         public @Nullable CharSequence qwacIdentity;
-
-        ViewParams() {
-            this.summary = "";
-            this.details = "";
-        }
     }
 
     private final Context mContext;
@@ -86,10 +83,11 @@
     }
 
     public void setParams(ViewParams params) {
-        boolean visible = params.summary.length() != 0 || params.details.length() != 0;
+        boolean visible = params.summary != null || params.details != null;
         setVisibility(visible ? VISIBLE : GONE);
         if (!visible) return;
 
+        DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
         mIcon.setImageResource(params.iconResId);
 
         ImageViewCompat.setImageTintList(
@@ -100,20 +98,16 @@
                                 mContext, R.color.default_icon_color_tint_list));
 
         mSummary.setText(params.summary);
-        mSummary.setVisibility(params.summary.length() != 0 ? VISIBLE : GONE);
-
-        if (params.details.length() != 0) {
-            if (params.summary.length() == 0) {
-                // The only case where we have details but no summary is when the IdentityInfo's
-                // SiteIdentityStatus is PageInfo::SITE_IDENTITY_STATUS_INTERNAL_PAGE. We shouldn't
-                // display a "Learn more" link for that.
-                mDetails.setText(params.details);
-            } else {
-                mDetails.setText(
-                        buildTextWithLink(params.details, mContext.getString(R.string.learn_more)));
-            }
+        mSummary.setVisibility(params.summary != null ? VISIBLE : GONE);
+        if (params.summary != null && params.details != null) {
+            mSummary.setPadding(0, 0, 0, ViewUtils.dpToPx(displayMetrics, 4));
         }
-        mDetails.setVisibility(params.details.length() != 0 ? VISIBLE : GONE);
+
+        if (params.details != null) {
+            mDetails.setText(
+                    buildTextWithLink(params.details, mContext.getString(R.string.learn_more)));
+        }
+        mDetails.setVisibility(params.details != null ? VISIBLE : GONE);
         mDetails.setOnClickListener(this);
 
         if (params.resetDecisionsCallback != null) {
diff --git a/components/page_info/android/page_info_controller_android.cc b/components/page_info/android/page_info_controller_android.cc
index 9563010..d42b5d1 100644
--- a/components/page_info/android/page_info_controller_android.cc
+++ b/components/page_info/android/page_info_controller_android.cc
@@ -107,15 +107,7 @@
       GetSecurityDescription(identity_info);
 
   if (base::FeatureList::IsEnabled(net::features::kVerifyQWACs)) {
-    if (PageInfo::IsFileOrInternalPage(url_)) {
-      // On Desktop, when PageInfo::IsFileOrInternalPage returns true, an
-      // InternalPageInfoBubbleView is used rather than the regular
-      // PageInfoBubbleView. That view displays only an info icon and a summary
-      // string.
-      Java_PageInfoController_showConnectionSecurityInfo(env,
-                                                         controller_jobject_);
-    } else if (security_description->summary_style ==
-               SecuritySummaryColor::GREEN) {
+    if (security_description->summary_style == SecuritySummaryColor::GREEN) {
       // Have the controller set up the button that will show the connection
       // security subpage.
       Java_PageInfoController_showOpenSecurityPageButton(
diff --git a/components/page_info/page_info.cc b/components/page_info/page_info.cc
index 2c01f0b..06f6c5dc 100644
--- a/components/page_info/page_info.cc
+++ b/components/page_info/page_info.cc
@@ -55,6 +55,7 @@
 #include "components/permissions/request_type.h"
 #include "components/privacy_sandbox/privacy_sandbox_features.h"
 #include "components/safe_browsing/buildflags.h"
+#include "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h"
 #include "components/safe_browsing/core/common/proto/csd.pb.h"
 #include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h"
 #include "components/ssl_errors/error_info.h"
@@ -779,6 +780,19 @@
     constraints.set_track_last_visit_for_autoexpiration(true);
   }
 
+  // If notification permission changes from allowed to not allowed, log the
+  // histogram.
+  if (type == ContentSettingsType::NOTIFICATIONS &&
+      setting_old == CONTENT_SETTING_ALLOW &&
+      (!setting ||
+       ToContentSettingForMetrics(info, setting) == CONTENT_SETTING_ASK ||
+       ToContentSettingForMetrics(info, setting) == CONTENT_SETTING_BLOCK)) {
+    safe_browsing::SafeBrowsingMetricsCollector::
+        LogSafeBrowsingNotificationRevocationSourceHistogram(
+            safe_browsing::NotificationRevocationSource::
+                kUserManuallyChangedSiteSetting);
+  }
+
   map->SetNarrowestContentSetting(primary_url, site_url_, type, setting,
                                   constraints);
 
diff --git a/components/page_info/page_info_ui.cc b/components/page_info/page_info_ui.cc
index 5f7e70aa..9ab09f4 100644
--- a/components/page_info/page_info_ui.cc
+++ b/components/page_info/page_info_ui.cc
@@ -882,13 +882,8 @@
 // static
 int PageInfoUI::GetIdentityIconID(PageInfo::SiteIdentityStatus status) {
   switch (status) {
-    case PageInfo::SITE_IDENTITY_STATUS_INTERNAL_PAGE:
-      if (base::FeatureList::IsEnabled(net::features::kVerifyQWACs)) {
-        return IDR_PAGEINFO_INTERNAL;
-      } else {
-        return IDR_PAGEINFO_GOOD;
-      }
     case PageInfo::SITE_IDENTITY_STATUS_UNKNOWN:
+    case PageInfo::SITE_IDENTITY_STATUS_INTERNAL_PAGE:
     case PageInfo::SITE_IDENTITY_STATUS_CERT:
     case PageInfo::SITE_IDENTITY_STATUS_EV_CERT:
     case PageInfo::SITE_IDENTITY_STATUS_1QWAC_CERT:
diff --git a/components/page_load_metrics/renderer/metrics_render_frame_observer.cc b/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
index a6da825..957bc917b 100644
--- a/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
+++ b/components/page_load_metrics/renderer/metrics_render_frame_observer.cc
@@ -127,6 +127,16 @@
     render_frame->SetLoadFromMemoryCacheCallback(base::BindRepeating(
         &MetricsRenderFrameObserver::DidLoadResourceFromMemoryCache,
         weak_factory_.GetWeakPtr()));
+
+    render_frame->SetDidStartResponseCallback(
+        base::BindRepeating(&MetricsRenderFrameObserver::DidStartResponse,
+                            weak_factory_.GetWeakPtr()));
+    render_frame->SetDidCompleteResponseCallback(
+        base::BindRepeating(&MetricsRenderFrameObserver::DidCompleteResponse,
+                            weak_factory_.GetWeakPtr()));
+    render_frame->SetDidCancelResponseCallback(
+        base::BindRepeating(&MetricsRenderFrameObserver::DidCancelResponse,
+                            weak_factory_.GetWeakPtr()));
   }
 }
 
diff --git a/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc b/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc
index 6fb28ead..81e55c7 100644
--- a/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc
+++ b/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc
@@ -363,7 +363,6 @@
                   int srcY) override {
     return false;
   }
-  void FlushPendingSkiaOps() override {}
 
  private:
   gpu::Mailbox mailbox_;
diff --git a/components/passage_embeddings/passage_embedder_model_observer.cc b/components/passage_embeddings/passage_embedder_model_observer.cc
index 11e02625..af791df 100644
--- a/components/passage_embeddings/passage_embedder_model_observer.cc
+++ b/components/passage_embeddings/passage_embedder_model_observer.cc
@@ -4,6 +4,7 @@
 
 #include "components/passage_embeddings/passage_embedder_model_observer.h"
 
+#include "base/task/thread_pool.h"
 #include "components/optimization_guide/core/delivery/optimization_guide_model_provider.h"
 #include "components/passage_embeddings/passage_embeddings_service_controller.h"
 
@@ -23,7 +24,10 @@
   if (model_provider_) {
     model_provider_->AddObserverForOptimizationTargetModel(
         target_,
-        /*model_metadata=*/std::nullopt, this);
+        /*model_metadata=*/std::nullopt,
+        base::ThreadPool::CreateSequencedTaskRunner(
+            {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
+        this);
   }
 }
 
diff --git a/components/passage_embeddings/passage_embedder_model_observer_unittest.cc b/components/passage_embeddings/passage_embedder_model_observer_unittest.cc
index f6bf4083..733a3da8 100644
--- a/components/passage_embeddings/passage_embedder_model_observer_unittest.cc
+++ b/components/passage_embeddings/passage_embedder_model_observer_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/memory/raw_ptr.h"
+#include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "components/optimization_guide/core/delivery/test_optimization_guide_model_provider.h"
 #include "components/passage_embeddings/passage_embeddings_service_controller.h"
@@ -49,6 +50,7 @@
   void AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OptimizationTarget optimization_target,
       const std::optional<optimization_guide::proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       optimization_guide::OptimizationTargetModelObserver* observer) override {
     target_observed_future_->SetValue(
         optimization_target ==
@@ -83,6 +85,7 @@
     }
   }
 
+  base::test::TaskEnvironment task_environment_;
   raw_ptr<base::test::TestFuture<bool>> target_observed_future_;
   base::ObserverList<optimization_guide::OptimizationTargetModelObserver>
       observer_list_;
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc
index e400b8e..c7e2ec2 100644
--- a/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -488,7 +488,7 @@
 
 void PasswordAutofillManager::ShowSuggestions(
     const autofill::TriggeringField& field) {
-  if (autofill::IsAutofillManuallyTriggered(field.trigger_source)) {
+  if (autofill::IsPasswordsAutofillManuallyTriggered(field.trigger_source)) {
     if (!manual_fallback_flow_) {
       manual_fallback_flow_ = std::make_unique<PasswordManualFallbackFlow>(
           password_manager_driver_, autofill_client_, password_client_,
diff --git a/components/password_manager/ios/shared_password_controller_unittest.mm b/components/password_manager/ios/shared_password_controller_unittest.mm
index c876cca..cd3858e 100644
--- a/components/password_manager/ios/shared_password_controller_unittest.mm
+++ b/components/password_manager/ios/shared_password_controller_unittest.mm
@@ -219,9 +219,8 @@
 
     PasswordGenerationFrameHelper* password_generation_helper_ptr =
         &password_generation_helper_;
-    OCMStub(
-        [driver_helper_ PasswordGenerationHelper:static_cast<web::WebFrame*>(
-                                                     [OCMArg anyPointer])])
+    OCMStub([driver_helper_
+                PasswordGenerationHelper:ios::OCM::AnyPointer<web::WebFrame>()])
         .andReturn(password_generation_helper_ptr);
 
     EXPECT_CALL(password_manager_, GetClient)
@@ -236,8 +235,8 @@
         web::FakeWebFrame::CreateMainWebFrame(GURL(kTestURL));
     dummy_driver_ = IOSPasswordManagerDriverFactory::GetRetainableDriver(
         &web_state_, dummy_web_frame.get());
-    OCMStub([driver_helper_ PasswordManagerDriver:static_cast<web::WebFrame*>(
-                                                      [OCMArg anyPointer])])
+    OCMStub([driver_helper_
+                PasswordManagerDriver:ios::OCM::AnyPointer<web::WebFrame>()])
         .andReturn(dummy_driver_.get());
   }
 
diff --git a/components/pdf/renderer/pdf_view_web_plugin_client.cc b/components/pdf/renderer/pdf_view_web_plugin_client.cc
index 0f26f6a1..6ca41ba 100644
--- a/components/pdf/renderer/pdf_view_web_plugin_client.cc
+++ b/components/pdf/renderer/pdf_view_web_plugin_client.cc
@@ -107,8 +107,9 @@
   v8::Local<v8::Value> converted_message =
       v8_value_converter_->ToV8Value(message, context);
 
-  plugin_container_->EnqueueMessageEvent(
+  blink::WebDOMMessageEvent dom_message(
       blink::WebSerializedScriptValue::Serialize(isolate_, converted_message));
+  plugin_container_->EnqueueMessageEvent(dom_message);
 }
 
 void PdfViewWebPluginClient::Invalidate() {
diff --git a/components/performance_manager/decorators/frame_input_state_decorator.cc b/components/performance_manager/decorators/frame_input_state_decorator.cc
index a3faa9b..d754b221 100644
--- a/components/performance_manager/decorators/frame_input_state_decorator.cc
+++ b/components/performance_manager/decorators/frame_input_state_decorator.cc
@@ -34,6 +34,46 @@
 
 }  // namespace
 
+// InputObserver receives input events from content::RenderWidgetHost and
+// determines the current InputScenario for a frame.
+class FrameInputStateDecorator::InputObserver
+    : public content::RenderWidgetHost::InputEventObserver,
+      public content::RenderWidgetHostObserver {
+ public:
+  InputObserver(FrameInputStateDecorator* decorator,
+                const FrameNode* frame_node,
+                RenderFrameHostProxy proxy);
+  ~InputObserver() override;
+
+  // content::RenderWidgetHost::InputEventObserver:
+  void OnInputEvent(const content::RenderWidgetHost& rwh,
+                    const blink::WebInputEvent& event) override;
+
+  // content::RenderWidgetHostObserver:
+  void RenderWidgetHostDestroyed(content::RenderWidgetHost* rwh) override;
+
+ private:
+  void OnInputInactiveTimer();
+  void OnKeyEvent(const blink::WebInputEvent& event);
+  void OnScrollEvent(const blink::WebInputEvent& event);
+  void OnTapEvent(const blink::WebInputEvent& event);
+
+  raw_ptr<FrameInputStateDecorator> decorator_;
+  raw_ptr<const FrameNode> frame_node_;
+  base::OneShotTimer timer_;
+
+  // Input detection.
+  base::TimeTicks last_key_event_time_;
+  InputScenario input_scenario_ = InputScenario::kNoInput;
+
+  base::ScopedObservation<content::RenderWidgetHost,
+                          content::RenderWidgetHost::InputEventObserver>
+      input_observation_{this};
+  base::ScopedObservation<content::RenderWidgetHost,
+                          content::RenderWidgetHostObserver>
+      rwh_observation_{this};
+};
+
 FrameInputStateDecorator::Data::Data(const FrameNode* frame_node) {
   input_observer_ = std::make_unique<InputObserver>(
       FrameInputStateDecorator::GetFromGraph(frame_node->GetGraph()),
@@ -96,46 +136,6 @@
   observers_.RemoveObserver(observer);
 }
 
-// InputObserver receives input events from content::RenderWidgetHost and
-// determines the current InputScenario for a frame.
-class FrameInputStateDecorator::InputObserver
-    : public content::RenderWidgetHost::InputEventObserver,
-      public content::RenderWidgetHostObserver {
- public:
-  InputObserver(FrameInputStateDecorator* decorator,
-                const FrameNode* frame_node,
-                RenderFrameHostProxy proxy);
-  ~InputObserver() override;
-
-  // content::RenderWidgetHost::InputEventObserver:
-  void OnInputEvent(const content::RenderWidgetHost& rwh,
-                    const blink::WebInputEvent& event) override;
-
-  // content::RenderWidgetHostObserver:
-  void RenderWidgetHostDestroyed(content::RenderWidgetHost* rwh) override;
-
- private:
-  void OnInputInactiveTimer();
-  void OnKeyEvent(const blink::WebInputEvent& event);
-  void OnScrollEvent(const blink::WebInputEvent& event);
-  void OnTapEvent(const blink::WebInputEvent& event);
-
-  raw_ptr<FrameInputStateDecorator> decorator_;
-  raw_ptr<const FrameNode> frame_node_;
-  base::OneShotTimer timer_;
-
-  // Input detection.
-  base::TimeTicks last_key_event_time_;
-  InputScenario input_scenario_ = InputScenario::kNoInput;
-
-  base::ScopedObservation<content::RenderWidgetHost,
-                          content::RenderWidgetHost::InputEventObserver>
-      input_observation_{this};
-  base::ScopedObservation<content::RenderWidgetHost,
-                          content::RenderWidgetHostObserver>
-      rwh_observation_{this};
-};
-
 FrameInputStateDecorator::InputObserver::InputObserver(
     FrameInputStateDecorator* decorator,
     const FrameNode* frame_node,
diff --git a/components/permissions/android/bluetooth_chooser_android.cc b/components/permissions/android/bluetooth_chooser_android.cc
index 2c11c69..47afc24c 100644
--- a/components/permissions/android/bluetooth_chooser_android.cc
+++ b/components/permissions/android/bluetooth_chooser_android.cc
@@ -184,6 +184,19 @@
                      "");
 }
 
+// static
+std::unique_ptr<BluetoothChooserAndroid>
+BluetoothChooserAndroid::CreateForTesting(
+    content::RenderFrameHost* frame,
+    const EventHandler& event_handler,
+    std::unique_ptr<BluetoothChooserAndroidDelegate> delegate,
+    CreateJavaDialogCallback create_java_dialog_callback) {
+  // Using `new` to access a non-public constructor.
+  return base::WrapUnique(
+      new BluetoothChooserAndroid(frame, event_handler, std::move(delegate),
+                                  std::move(create_java_dialog_callback)));
+}
+
 void BluetoothChooserAndroid::OpenURL(const char* url) {
   web_contents_->OpenURL(
       content::OpenURLParams(GURL(url), content::Referrer(),
diff --git a/components/permissions/android/bluetooth_chooser_android.h b/components/permissions/android/bluetooth_chooser_android.h
index a4030d4..23d40a3 100644
--- a/components/permissions/android/bluetooth_chooser_android.h
+++ b/components/permissions/android/bluetooth_chooser_android.h
@@ -70,12 +70,7 @@
       content::RenderFrameHost* frame,
       const EventHandler& event_handler,
       std::unique_ptr<BluetoothChooserAndroidDelegate> delegate,
-      CreateJavaDialogCallback create_java_dialog_callback) {
-    // Using `new` to access a non-public constructor.
-    return base::WrapUnique(
-        new BluetoothChooserAndroid(frame, event_handler, std::move(delegate),
-                                    std::move(create_java_dialog_callback)));
-  }
+      CreateJavaDialogCallback create_java_dialog_callback);
 
  private:
   BluetoothChooserAndroid(
diff --git a/components/permissions/android/bluetooth_scanning_prompt_android.cc b/components/permissions/android/bluetooth_scanning_prompt_android.cc
index 91ca1596..8efa5286 100644
--- a/components/permissions/android/bluetooth_scanning_prompt_android.cc
+++ b/components/permissions/android/bluetooth_scanning_prompt_android.cc
@@ -111,4 +111,17 @@
   NOTREACHED();
 }
 
+// static
+std::unique_ptr<BluetoothScanningPromptAndroid>
+BluetoothScanningPromptAndroid::CreateForTesting(
+    content::RenderFrameHost* frame,
+    const EventHandler& event_handler,
+    std::unique_ptr<BluetoothScanningPromptAndroidDelegate> delegate,
+    CreateJavaDialogCallback create_java_dialog_callback) {
+  // Using `new` to access a non-public constructor.
+  return base::WrapUnique(new BluetoothScanningPromptAndroid(
+      frame, event_handler, std::move(delegate),
+      std::move(create_java_dialog_callback)));
+}
+
 }  // namespace permissions
diff --git a/components/permissions/android/bluetooth_scanning_prompt_android.h b/components/permissions/android/bluetooth_scanning_prompt_android.h
index 50973704..773434f10d 100644
--- a/components/permissions/android/bluetooth_scanning_prompt_android.h
+++ b/components/permissions/android/bluetooth_scanning_prompt_android.h
@@ -56,12 +56,7 @@
       content::RenderFrameHost* frame,
       const EventHandler& event_handler,
       std::unique_ptr<BluetoothScanningPromptAndroidDelegate> delegate,
-      CreateJavaDialogCallback create_java_dialog_callback) {
-    // Using `new` to access a non-public constructor.
-    return base::WrapUnique(new BluetoothScanningPromptAndroid(
-        frame, event_handler, std::move(delegate),
-        std::move(create_java_dialog_callback)));
-  }
+      CreateJavaDialogCallback create_java_dialog_callback);
 
  private:
   BluetoothScanningPromptAndroid(
diff --git a/components/permissions/contexts/geolocation_permission_context_android.cc b/components/permissions/contexts/geolocation_permission_context_android.cc
index dc02a17..b3c1637 100644
--- a/components/permissions/contexts/geolocation_permission_context_android.cc
+++ b/components/permissions/contexts/geolocation_permission_context_android.cc
@@ -139,7 +139,8 @@
                                 *request_data, render_frame_host)
                                 .status;
   if (!request_data->IsEmbeddedPermissionElementInitiated() &&
-      status == PermissionStatus::GRANTED &&
+      (status == PermissionStatus::GRANTED ||
+       status == PermissionStatus::UNSATISFIED_OPTIONS) &&
       ShouldRepromptUserForPermissions(web_contents,
                                        {content_settings_type()}) ==
           PermissionRepromptState::kShow) {
diff --git a/components/permissions/permission_context_base.cc b/components/permissions/permission_context_base.cc
index f788228..cef2f92 100644
--- a/components/permissions/permission_context_base.cc
+++ b/components/permissions/permission_context_base.cc
@@ -152,8 +152,10 @@
   bool status_ignorable = PermissionUtil::CanPermissionRequestIgnoreStatus(
       request_data, result.source);
 
-  if (!status_ignorable && (result.status == PermissionStatus::GRANTED ||
-                            result.status == PermissionStatus::DENIED)) {
+  if (!status_ignorable &&
+      (result.status == PermissionStatus::GRANTED ||
+       result.status == PermissionStatus::DENIED ||
+       result.status == PermissionStatus::UNSATISFIED_OPTIONS)) {
     static constexpr char kResetInstructions[] =
         " This can be reset in "
 #if BUILDFLAG(IS_ANDROID)
diff --git a/components/permissions/prediction_service/permissions_ai_encoder_base.cc b/components/permissions/prediction_service/permissions_ai_encoder_base.cc
index 3124fa2..b69cb18 100644
--- a/components/permissions/prediction_service/permissions_ai_encoder_base.cc
+++ b/components/permissions/prediction_service/permissions_ai_encoder_base.cc
@@ -82,12 +82,6 @@
   return PermissionRequestRelevance::kVeryHigh;
 }
 
-template <typename EncoderInput>
-base::TaskPriority
-PermissionsAiEncoderBase<EncoderInput>::GetModelLoadingTaskPriority() const {
-  return base::TaskPriority::USER_VISIBLE;
-}
-
 // Template instantiation for the Aiv3/Aiv4 model handlers.
 template class PermissionsAiEncoderBase<const PermissionsAiv3ExecutorInput&>;
 template class PermissionsAiEncoderBase<const PermissionsAiv4ExecutorInput&>;
diff --git a/components/permissions/prediction_service/permissions_ai_encoder_base.h b/components/permissions/prediction_service/permissions_ai_encoder_base.h
index 45f96d2..fd72200b 100644
--- a/components/permissions/prediction_service/permissions_ai_encoder_base.h
+++ b/components/permissions/prediction_service/permissions_ai_encoder_base.h
@@ -40,7 +40,6 @@
                   const ModelInput& input) override = 0;
   std::optional<ModelOutput> Postprocess(
       const std::vector<const TfLiteTensor*>& output_tensors) override;
-  base::TaskPriority GetModelLoadingTaskPriority() const override;
 
   RequestType request_type() const { return request_type_; }
   std::array<float, 4>& relevance_thresholds() { return relevance_thresholds_; }
diff --git a/components/permissions/prediction_service/permissions_aiv3_handler.cc b/components/permissions/prediction_service/permissions_aiv3_handler.cc
index 199e688..7c9f00ef 100644
--- a/components/permissions/prediction_service/permissions_aiv3_handler.cc
+++ b/components/permissions/prediction_service/permissions_aiv3_handler.cc
@@ -38,6 +38,7 @@
           /*model_inference_timeout=*/std::nullopt,
           optimization_target,
           /*model_metadata=*/std::nullopt,
+          /*model_loading_task_runner=*/nullptr,
           reply_task_runner) {}
 
 PermissionsAiv3Handler::PermissionsAiv3Handler(
diff --git a/components/permissions/prediction_service/permissions_aiv4_handler.cc b/components/permissions/prediction_service/permissions_aiv4_handler.cc
index f6a5fd0..22947f5 100644
--- a/components/permissions/prediction_service/permissions_aiv4_handler.cc
+++ b/components/permissions/prediction_service/permissions_aiv4_handler.cc
@@ -36,6 +36,7 @@
           /*model_inference_timeout=*/std::nullopt,
           optimization_target,
           /*model_metadata=*/std::nullopt,
+          /*model_loading_task_runner=*/nullptr,
           reply_task_runner) {}
 
 PermissionsAiv4Handler::PermissionsAiv4Handler(
diff --git a/components/plus_addresses/core/browser/BUILD.gn b/components/plus_addresses/core/browser/BUILD.gn
index 78ba046..0e3d7d3 100644
--- a/components/plus_addresses/core/browser/BUILD.gn
+++ b/components/plus_addresses/core/browser/BUILD.gn
@@ -83,8 +83,8 @@
     "plus_address_service.h",
     "plus_address_service_impl.cc",
     "plus_address_service_impl.h",
-    "plus_address_suggestion_generator.cc",
-    "plus_address_suggestion_generator.h",
+    "plus_address_suggestion_helper.cc",
+    "plus_address_suggestion_helper.h",
     "plus_address_ui_utils.cc",
     "plus_address_ui_utils.h",
   ]
@@ -139,7 +139,7 @@
     "plus_address_parsing_utils_unittest.cc",
     "plus_address_preallocator_unittest.cc",
     "plus_address_service_impl_unittest.cc",
-    "plus_address_suggestion_generator_unittest.cc",
+    "plus_address_suggestion_helper_unittest.cc",
   ]
 
   deps = [
diff --git a/components/plus_addresses/core/browser/plus_address_service_impl.cc b/components/plus_addresses/core/browser/plus_address_service_impl.cc
index c136516..409d528a 100644
--- a/components/plus_addresses/core/browser/plus_address_service_impl.cc
+++ b/components/plus_addresses/core/browser/plus_address_service_impl.cc
@@ -39,7 +39,7 @@
 #include "components/plus_addresses/core/browser/plus_address_http_client_impl.h"
 #include "components/plus_addresses/core/browser/plus_address_jit_allocator.h"
 #include "components/plus_addresses/core/browser/plus_address_preallocator.h"
-#include "components/plus_addresses/core/browser/plus_address_suggestion_generator.h"
+#include "components/plus_addresses/core/browser/plus_address_suggestion_helper.h"
 #include "components/plus_addresses/core/browser/plus_address_types.h"
 #include "components/plus_addresses/core/browser/plus_address_ui_utils.h"
 #include "components/plus_addresses/core/browser/settings/plus_address_setting_service.h"
@@ -336,9 +336,9 @@
   const bool is_creation_enabled =
       IsPlusAddressCreationEnabled(origin, is_off_the_record);
   std::vector<Suggestion> suggestions =
-      PlusAddressSuggestionGenerator(&setting_service_.get(),
-                                     plus_address_allocator_.get(),
-                                     std::move(origin))
+      PlusAddressSuggestionHelper(&setting_service_.get(),
+                                  plus_address_allocator_.get(),
+                                  std::move(origin))
           .GetSuggestions(plus_addresses, is_creation_enabled, focused_form,
                           focused_field, form_field_type_groups,
                           focused_form_classification, trigger_source);
@@ -357,7 +357,7 @@
 }
 
 Suggestion PlusAddressServiceImpl::GetManagePlusAddressSuggestion() const {
-  return PlusAddressSuggestionGenerator::GetManagePlusAddressSuggestion();
+  return PlusAddressSuggestionHelper::GetManagePlusAddressSuggestion();
 }
 
 void PlusAddressServiceImpl::ReservePlusAddress(
@@ -643,9 +643,9 @@
       SuggestionEvent::kRefreshPlusAddressInlineClicked);
   std::vector<Suggestion> updated_suggestions(current_suggestions.begin(),
                                               current_suggestions.end());
-  PlusAddressSuggestionGenerator(&setting_service_.get(),
-                                 plus_address_allocator_.get(),
-                                 last_committed_primary_main_frame_origin)
+  PlusAddressSuggestionHelper(&setting_service_.get(),
+                              plus_address_allocator_.get(),
+                              last_committed_primary_main_frame_origin)
       .RefreshPlusAddressForSuggestion(
           updated_suggestions[current_suggestion_index]);
   std::move(update_suggestions_callback)
@@ -682,7 +682,7 @@
          const PlusProfileOrError& profile_or_error) {
         if (!profile_or_error.has_value()) {
           suggestions[suggestion_index] =
-              PlusAddressSuggestionGenerator::GetPlusAddressErrorSuggestion(
+              PlusAddressSuggestionHelper::GetPlusAddressErrorSuggestion(
                   profile_or_error.error());
           metrics::RecordAutofillSuggestionEvent(
               SuggestionEvent::kErrorDuringReserve);
@@ -692,7 +692,7 @@
                        kPlusAddressUpdatedInBrowserProcess);
           return;
         }
-        PlusAddressSuggestionGenerator::SetSuggestedPlusAddressForSuggestion(
+        PlusAddressSuggestionHelper::SetSuggestedPlusAddressForSuggestion(
             profile_or_error->plus_address, suggestions[suggestion_index]);
         std::move(update_callback)
             .Run(std::move(suggestions),
@@ -726,7 +726,7 @@
   // First, update the suggestions to show a loading state.
   std::vector<Suggestion> updated_suggestions(current_suggestions.begin(),
                                               current_suggestions.end());
-  PlusAddressSuggestionGenerator::SetLoadingStateForSuggestion(
+  PlusAddressSuggestionHelper::SetLoadingStateForSuggestion(
       /*is_loading=*/true, updated_suggestions[current_suggestion_index]);
   std::move(update_suggestions_callback)
       .Run(
diff --git a/components/plus_addresses/core/browser/plus_address_service_impl_unittest.cc b/components/plus_addresses/core/browser/plus_address_service_impl_unittest.cc
index 9644667b..813de6a 100644
--- a/components/plus_addresses/core/browser/plus_address_service_impl_unittest.cc
+++ b/components/plus_addresses/core/browser/plus_address_service_impl_unittest.cc
@@ -48,7 +48,7 @@
 #include "components/plus_addresses/core/browser/plus_address_hats_utils.h"
 #include "components/plus_addresses/core/browser/plus_address_http_client_impl.h"
 #include "components/plus_addresses/core/browser/plus_address_preallocator.h"
-#include "components/plus_addresses/core/browser/plus_address_suggestion_generator.h"
+#include "components/plus_addresses/core/browser/plus_address_suggestion_helper.h"
 #include "components/plus_addresses/core/browser/plus_address_test_environment.h"
 #include "components/plus_addresses/core/browser/plus_address_test_utils.h"
 #include "components/plus_addresses/core/browser/plus_address_types.h"
@@ -705,7 +705,7 @@
   EXPECT_CALL(
       callback,
       Run(ElementsAre(
-              PlusAddressSuggestionGenerator::GetPlusAddressErrorSuggestion(
+              PlusAddressSuggestionHelper::GetPlusAddressErrorSuggestion(
                   PlusAddressRequestError::AsNetworkError(
                       net::HTTP_REQUEST_TIMEOUT))),
           AutofillSuggestionTriggerSource::
@@ -1709,7 +1709,7 @@
 // click is a signup form if the username field is the focused field, but that
 // filling suggestions are always offered.
 // TODO(crbug.com/322279583): Move to
-// `plus_address_suggestion_generator_unittest`, since this should make it
+// `plus_address_suggestion_helper_unittest`, since this should make it
 // easier to test.
 TEST_F(PlusAddressSuggestionsTest, SuggestionsOnPasswordFormsUsernameField) {
   const PlusProfile profile = test::CreatePlusProfile();
diff --git a/components/plus_addresses/core/browser/plus_address_suggestion_generator.cc b/components/plus_addresses/core/browser/plus_address_suggestion_helper.cc
similarity index 94%
rename from components/plus_addresses/core/browser/plus_address_suggestion_generator.cc
rename to components/plus_addresses/core/browser/plus_address_suggestion_helper.cc
index 2c0d32b..e4e9683 100644
--- a/components/plus_addresses/core/browser/plus_address_suggestion_generator.cc
+++ b/components/plus_addresses/core/browser/plus_address_suggestion_helper.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/plus_addresses/core/browser/plus_address_suggestion_generator.h"
+#include "components/plus_addresses/core/browser/plus_address_suggestion_helper.h"
 
 #include <string>
 #include <utility>
@@ -161,7 +161,7 @@
 
 }  // namespace
 
-PlusAddressSuggestionGenerator::PlusAddressSuggestionGenerator(
+PlusAddressSuggestionHelper::PlusAddressSuggestionHelper(
     const PlusAddressSettingService* setting_service,
     PlusAddressAllocator* allocator,
     url::Origin origin)
@@ -169,10 +169,9 @@
       allocator_(CHECK_DEREF(allocator)),
       origin_(std::move(origin)) {}
 
-PlusAddressSuggestionGenerator::~PlusAddressSuggestionGenerator() = default;
+PlusAddressSuggestionHelper::~PlusAddressSuggestionHelper() = default;
 
-std::vector<autofill::Suggestion>
-PlusAddressSuggestionGenerator::GetSuggestions(
+std::vector<autofill::Suggestion> PlusAddressSuggestionHelper::GetSuggestions(
     const std::vector<std::string>& affiliated_plus_addresses,
     bool is_creation_enabled,
     const autofill::FormData& focused_form,
@@ -228,7 +227,7 @@
   return suggestions;
 }
 
-void PlusAddressSuggestionGenerator::RefreshPlusAddressForSuggestion(
+void PlusAddressSuggestionHelper::RefreshPlusAddressForSuggestion(
     Suggestion& suggestion) {
   CHECK(IsInlineGenerationEnabled());
   suggestion =
@@ -236,7 +235,7 @@
 }
 
 // static
-Suggestion PlusAddressSuggestionGenerator::GetManagePlusAddressSuggestion() {
+Suggestion PlusAddressSuggestionHelper::GetManagePlusAddressSuggestion() {
   Suggestion suggestion(
       l10n_util::GetStringUTF16(IDS_PLUS_ADDRESS_MANAGE_PLUS_ADDRESSES_TEXT),
       SuggestionType::kManagePlusAddress);
@@ -245,7 +244,7 @@
 }
 
 // static
-Suggestion PlusAddressSuggestionGenerator::GetPlusAddressErrorSuggestion(
+Suggestion PlusAddressSuggestionHelper::GetPlusAddressErrorSuggestion(
     const PlusAddressRequestError& error) {
   Suggestion suggestion(
       l10n_util::GetStringUTF16(IDS_PLUS_ADDRESS_CREATE_SUGGESTION_MAIN_TEXT),
@@ -275,7 +274,7 @@
 }
 
 // static
-void PlusAddressSuggestionGenerator::SetSuggestedPlusAddressForSuggestion(
+void PlusAddressSuggestionHelper::SetSuggestedPlusAddressForSuggestion(
     const PlusAddress& plus_address,
     autofill::Suggestion& suggestion) {
   suggestion.payload =
@@ -284,7 +283,7 @@
 }
 
 // static
-void PlusAddressSuggestionGenerator::SetLoadingStateForSuggestion(
+void PlusAddressSuggestionHelper::SetLoadingStateForSuggestion(
     bool is_loading,
     autofill::Suggestion& suggestion) {
   suggestion.is_loading = Suggestion::IsLoading(is_loading);
@@ -298,7 +297,7 @@
 }
 
 autofill::Suggestion
-PlusAddressSuggestionGenerator::CreateNewPlusAddressSuggestion() {
+PlusAddressSuggestionHelper::CreateNewPlusAddressSuggestion() {
   if (IsInlineGenerationEnabled()) {
     return CreateNewPlusAddressInlineSuggestion(/*refreshed_suggestion=*/false);
   }
@@ -316,7 +315,7 @@
   return suggestion;
 }
 
-bool PlusAddressSuggestionGenerator::IsInlineGenerationEnabled() const {
+bool PlusAddressSuggestionHelper::IsInlineGenerationEnabled() const {
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
   if (!setting_service_->GetHasAcceptedNotice()) {
     return false;
@@ -328,7 +327,7 @@
 }
 
 autofill::Suggestion
-PlusAddressSuggestionGenerator::CreateNewPlusAddressInlineSuggestion(
+PlusAddressSuggestionHelper::CreateNewPlusAddressInlineSuggestion(
     bool refreshed_suggestion) {
   Suggestion suggestion(
       l10n_util::GetStringUTF16(IDS_PLUS_ADDRESS_CREATE_SUGGESTION_MAIN_TEXT),
diff --git a/components/plus_addresses/core/browser/plus_address_suggestion_generator.h b/components/plus_addresses/core/browser/plus_address_suggestion_helper.h
similarity index 92%
rename from components/plus_addresses/core/browser/plus_address_suggestion_generator.h
rename to components/plus_addresses/core/browser/plus_address_suggestion_helper.h
index 4e7246e..9e014845 100644
--- a/components/plus_addresses/core/browser/plus_address_suggestion_generator.h
+++ b/components/plus_addresses/core/browser/plus_address_suggestion_helper.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_PLUS_ADDRESSES_CORE_BROWSER_PLUS_ADDRESS_SUGGESTION_GENERATOR_H_
-#define COMPONENTS_PLUS_ADDRESSES_CORE_BROWSER_PLUS_ADDRESS_SUGGESTION_GENERATOR_H_
+#ifndef COMPONENTS_PLUS_ADDRESSES_CORE_BROWSER_PLUS_ADDRESS_SUGGESTION_HELPER_H_
+#define COMPONENTS_PLUS_ADDRESSES_CORE_BROWSER_PLUS_ADDRESS_SUGGESTION_HELPER_H_
 
 #include <vector>
 
@@ -26,15 +26,14 @@
 // Helper class for generation plus address suggestions. Objects of this class
 // are not intended to be saved into a member - instead, their lifetime should
 // be scoped to a method call that generates suggestions.
-class PlusAddressSuggestionGenerator final {
+class PlusAddressSuggestionHelper final {
   STACK_ALLOCATED();
 
  public:
-  PlusAddressSuggestionGenerator(
-      const PlusAddressSettingService* setting_service,
-      PlusAddressAllocator* allocator,
-      url::Origin origin);
-  ~PlusAddressSuggestionGenerator();
+  PlusAddressSuggestionHelper(const PlusAddressSettingService* setting_service,
+                              PlusAddressAllocator* allocator,
+                              url::Origin origin);
+  ~PlusAddressSuggestionHelper();
 
   // Returns the suggestions to be offered on the field in `focused_form` with
   // `focused_field_id` with Password Manager classification
@@ -101,4 +100,4 @@
 
 }  // namespace plus_addresses
 
-#endif  // COMPONENTS_PLUS_ADDRESSES_CORE_BROWSER_PLUS_ADDRESS_SUGGESTION_GENERATOR_H_
+#endif  // COMPONENTS_PLUS_ADDRESSES_CORE_BROWSER_PLUS_ADDRESS_SUGGESTION_HELPER_H_
diff --git a/components/plus_addresses/core/browser/plus_address_suggestion_generator_unittest.cc b/components/plus_addresses/core/browser/plus_address_suggestion_helper_unittest.cc
similarity index 90%
rename from components/plus_addresses/core/browser/plus_address_suggestion_generator_unittest.cc
rename to components/plus_addresses/core/browser/plus_address_suggestion_helper_unittest.cc
index 31483861..8a073df 100644
--- a/components/plus_addresses/core/browser/plus_address_suggestion_generator_unittest.cc
+++ b/components/plus_addresses/core/browser/plus_address_suggestion_helper_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/plus_addresses/core/browser/plus_address_suggestion_generator.h"
+#include "components/plus_addresses/core/browser/plus_address_suggestion_helper.h"
 
 #include <string>
 #include <utility>
@@ -85,9 +85,9 @@
       std::move(form), autofill::test::MakeLocalFrameToken());
 }
 
-class PlusAddressSuggestionGeneratorTest : public ::testing::Test {
+class PlusAddressSuggestionHelperTest : public ::testing::Test {
  public:
-  PlusAddressSuggestionGeneratorTest() = default;
+  PlusAddressSuggestionHelperTest() = default;
 
  protected:
   FakePlusAddressAllocator& allocator() { return allocator_; }
@@ -103,10 +103,10 @@
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 // Tests that an empty PlusAddressPayload is set if there are no cached plus
 // addresses.
-TEST_F(PlusAddressSuggestionGeneratorTest,
+TEST_F(PlusAddressSuggestionHelperTest,
        InlineGenerationWithoutPreallocatedAddresses) {
   allocator().set_is_next_allocation_synchronous(false);
-  PlusAddressSuggestionGenerator generator(
+  PlusAddressSuggestionHelper generator(
       &setting_service(), &allocator(),
       url::Origin::Create(GURL("https://foo.bar")));
   FormData form = CreateTestSignupFormData();
@@ -121,10 +121,10 @@
 
 // Tests that if there are cached plus addresses available, then one is set is
 // the PlusAddressPayload.
-TEST_F(PlusAddressSuggestionGeneratorTest,
+TEST_F(PlusAddressSuggestionHelperTest,
        InlineGenerationWithPreallocatedAddresses) {
   allocator().set_is_next_allocation_synchronous(true);
-  PlusAddressSuggestionGenerator generator(
+  PlusAddressSuggestionHelper generator(
       &setting_service(), &allocator(),
       url::Origin::Create(GURL("https://foo.bar")));
   FormData form = CreateTestSignupFormData();
@@ -138,13 +138,12 @@
                       *test::CreatePlusProfile().plus_address))));
 }
 
-TEST_F(PlusAddressSuggestionGeneratorTest,
-       SetSuggestedPlusAddressForSuggestion) {
+TEST_F(PlusAddressSuggestionHelperTest, SetSuggestedPlusAddressForSuggestion) {
   const PlusAddress plus_address("plus@foo.com");
   Suggestion suggestion(SuggestionType::kCreateNewPlusAddressInline);
   suggestion.payload = Suggestion::PlusAddressPayload();
   suggestion.is_loading = Suggestion::IsLoading(true);
-  PlusAddressSuggestionGenerator::SetSuggestedPlusAddressForSuggestion(
+  PlusAddressSuggestionHelper::SetSuggestedPlusAddressForSuggestion(
       plus_address, suggestion);
 
   EXPECT_FALSE(suggestion.is_loading);
@@ -152,9 +151,9 @@
             base::UTF8ToUTF16(*plus_address));
 }
 
-TEST_F(PlusAddressSuggestionGeneratorTest, GetPlusAddressErrorSuggestion) {
+TEST_F(PlusAddressSuggestionHelperTest, GetPlusAddressErrorSuggestion) {
   const Suggestion suggestion(
-      PlusAddressSuggestionGenerator::GetPlusAddressErrorSuggestion(
+      PlusAddressSuggestionHelper::GetPlusAddressErrorSuggestion(
           PlusAddressRequestError::AsNetworkError(net::HTTP_BAD_REQUEST)));
   EXPECT_EQ(suggestion.type, SuggestionType::kPlusAddressError);
   EXPECT_EQ(
@@ -169,14 +168,14 @@
           IDS_PLUS_ADDRESS_RESERVE_GENERIC_ERROR_TEXT)))));
 }
 
-TEST_F(PlusAddressSuggestionGeneratorTest,
+TEST_F(PlusAddressSuggestionHelperTest,
        GetPlusAddressErrorSuggestionForQuotaError) {
   const auto error =
       PlusAddressRequestError::AsNetworkError(net::HTTP_TOO_MANY_REQUESTS);
   ASSERT_TRUE(error.IsQuotaError());
 
   const Suggestion suggestion(
-      PlusAddressSuggestionGenerator::GetPlusAddressErrorSuggestion(error));
+      PlusAddressSuggestionHelper::GetPlusAddressErrorSuggestion(error));
   EXPECT_EQ(suggestion.type, SuggestionType::kPlusAddressError);
   EXPECT_EQ(
       suggestion.main_text.value,
@@ -192,18 +191,18 @@
 
 // Tests that suggestions in the `is_loading` state do not have a refresh
 // button and is not acceptable.
-TEST_F(PlusAddressSuggestionGeneratorTest, LoadingStateProperties) {
+TEST_F(PlusAddressSuggestionHelperTest, LoadingStateProperties) {
   Suggestion inline_suggestion(SuggestionType::kCreateNewPlusAddressInline);
   inline_suggestion.payload = Suggestion::PlusAddressPayload();
 
-  PlusAddressSuggestionGenerator::SetLoadingStateForSuggestion(
+  PlusAddressSuggestionHelper::SetLoadingStateForSuggestion(
       /*is_loading=*/true, inline_suggestion);
   EXPECT_TRUE(inline_suggestion.is_loading);
   EXPECT_FALSE(inline_suggestion.IsAcceptable());
   EXPECT_FALSE(inline_suggestion.GetPayload<Suggestion::PlusAddressPayload>()
                    .offer_refresh);
 
-  PlusAddressSuggestionGenerator::SetSuggestedPlusAddressForSuggestion(
+  PlusAddressSuggestionHelper::SetSuggestedPlusAddressForSuggestion(
       PlusAddress("foo@moo.com"), inline_suggestion);
   EXPECT_FALSE(inline_suggestion.is_loading);
   EXPECT_TRUE(inline_suggestion.GetPayload<Suggestion::PlusAddressPayload>()
@@ -213,9 +212,9 @@
 
 // Tests that creation is offered on fields where autofill was previously
 // triggered.
-TEST_F(PlusAddressSuggestionGeneratorTest,
+TEST_F(PlusAddressSuggestionHelperTest,
        CreationSuggestionOnPreviouslyAutofilledFields) {
-  PlusAddressSuggestionGenerator generator(
+  PlusAddressSuggestionHelper generator(
       &setting_service(), &allocator(),
       url::Origin::Create(GURL("https://foo.bar")));
 
@@ -247,10 +246,10 @@
 
 // Tests that the creation suggestion contains no labels if the notice has not
 // been accepted.
-TEST_F(PlusAddressSuggestionGeneratorTest, FirstTimeCreateSuggestion) {
+TEST_F(PlusAddressSuggestionHelperTest, FirstTimeCreateSuggestion) {
   setting_service().set_has_accepted_notice(false);
 
-  PlusAddressSuggestionGenerator generator(
+  PlusAddressSuggestionHelper generator(
       &setting_service(), &allocator(),
       url::Origin::Create(GURL("https://foo.bar")));
   FormData form = CreateTestSignupFormData();
@@ -265,8 +264,8 @@
 }
 
 // Tests that no creation suggestion is shown on a login form.
-TEST_F(PlusAddressSuggestionGeneratorTest, NoSuggestionsOnLoginForm) {
-  PlusAddressSuggestionGenerator generator(
+TEST_F(PlusAddressSuggestionHelperTest, NoSuggestionsOnLoginForm) {
+  PlusAddressSuggestionHelper generator(
       &setting_service(), &allocator(),
       url::Origin::Create(GURL("https://foo.bar")));
   const FormData login_form = SetGeneratedFrameTokenAndHostFormId(
@@ -292,9 +291,8 @@
 
 // Tests that creation is offered on forms classified by PWM as login forms if
 // they have name or address fields included.
-TEST_F(PlusAddressSuggestionGeneratorTest,
-       SuggestionsOnLoginFormWithNameFields) {
-  PlusAddressSuggestionGenerator generator(
+TEST_F(PlusAddressSuggestionHelperTest, SuggestionsOnLoginFormWithNameFields) {
+  PlusAddressSuggestionHelper generator(
       &setting_service(), &allocator(),
       url::Origin::Create(GURL("https://foo.bar")));
   FormData form = autofill::test::CreateTestPasswordFormData();
@@ -328,9 +326,9 @@
 
 // Tests that creation is offered on forms classified by PWM as login forms if
 // the password field is hidden.
-TEST_F(PlusAddressSuggestionGeneratorTest,
+TEST_F(PlusAddressSuggestionHelperTest,
        SuggestionsOnLoginFormWithHiddenPasswordField) {
-  PlusAddressSuggestionGenerator generator(
+  PlusAddressSuggestionHelper generator(
       &setting_service(), &allocator(),
       url::Origin::Create(GURL("https://foo.bar")));
   FormData form = autofill::test::CreateTestPasswordFormData();
@@ -355,9 +353,9 @@
 
 // Tests that filling is offered on fields where autofill was previously
 // triggered and prefix-matching is not applied.
-TEST_F(PlusAddressSuggestionGeneratorTest,
+TEST_F(PlusAddressSuggestionHelperTest,
        FillingSuggestionOnPreviouslyAutofilledFields) {
-  PlusAddressSuggestionGenerator generator(
+  PlusAddressSuggestionHelper generator(
       &setting_service(), &allocator(),
       url::Origin::Create(GURL("https://foo.bar")));
   const std::string plus_address = "test+plus@test.com";
diff --git a/components/policy/core/common/features.cc b/components/policy/core/common/features.cc
index f82d277c..48caa5c 100644
--- a/components/policy/core/common/features.cc
+++ b/components/policy/core/common/features.cc
@@ -30,8 +30,6 @@
     &kCustomPolicyRegistrationDelay, "PolicyRegistrationDelay", base::Hours(6)};
 
 // Used to add a captive portal check in SafeSitesNavigationThrottle.
-BASE_FEATURE(kSafeSitesCaptivePortalCheck,
-             "SafeSitesCaptivePortalCheck",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kSafeSitesCaptivePortalCheck, base::FEATURE_ENABLED_BY_DEFAULT);
 
 }  // namespace policy::features
diff --git a/components/policy/core/common/policy_logger.h b/components/policy/core/common/policy_logger.h
index d2adae8..5e60b151 100644
--- a/components/policy/core/common/policy_logger.h
+++ b/components/policy/core/common/policy_logger.h
@@ -65,7 +65,7 @@
 
 namespace policy {
 
-// Collects logs to be displayed in chrome://policy-logs.
+// Collects logs to be displayed in chrome://policy/logs.
 class POLICY_EXPORT PolicyLogger {
  public:
   class POLICY_EXPORT Log {
diff --git a/components/policy/resources/policy_templates_th.xtb b/components/policy/resources/policy_templates_th.xtb
index 513ac3d..7589885 100644
--- a/components/policy/resources/policy_templates_th.xtb
+++ b/components/policy/resources/policy_templates_th.xtb
@@ -6769,6 +6769,21 @@
 <translation id="5625771176514429288">แอป Chrome จะได้รับอนุญาตให้ทำงานในแพลตฟอร์มเหล่านี้</translation>
 <translation id="5627270992026554674">ไม่อนุญาตให้ครูใช้บัญชีรายชื่อของชั้นเรียนใน Google Classroom</translation>
 <translation id="5630352020869108293">คืนค่าเซสชันล่าสุด</translation>
+<translation id="5630952076819481843">การตั้งค่านโยบายนี้ช่วยให้คุณสร้างรายการรูปแบบ URL ที่ระบุเว็บไซต์ซึ่ง Chrome จะเลือกใบรับรองไคลเอ็นต์ให้โดยอัตโนมัติได้ ค่าจะเป็นอาร์เรย์ของพจนานุกรม JSON ที่มีรูปแบบเป็นสตริงซึ่งแต่ละรายการมีรูปแบบ <ph name="AUTO_SELECT_CERTIFICATE_FOR_URLS_EXAMPLE" /> โดยที่ <ph name="URL_PATTERN_PLACEHOLDER" /> เป็นรูปแบบการตั้งค่าเนื้อหา <ph name="FILTER_PLACEHOLDER" /> จำกัดใบรับรองไคลเอ็นต์ที่เบราว์เซอร์จะเลือกโดยอัตโนมัติ ระบบจะเลือกเฉพาะใบรับรองที่ตรงกับคำขอใบรับรองของเซิร์ฟเวอร์เท่านั้น โดยไม่คำนึงถึงตัวกรอง ใน Android Chrome จะเลือกได้เฉพาะใบรับรองไคลเอ็นต์ที่จัดสรรไว้เองเท่านั้น โดยจะเข้าถึงใบรับรองที่ติดตั้งไว้ในระดับระบบปฏิบัติการไม่ได้
+
+      ตัวอย่างการใช้งานส่วน <ph name="FILTER_PLACEHOLDER" />
+
+* เมื่อตั้งค่า <ph name="FILTER_PLACEHOLDER" /> เป็น <ph name="AUTO_SELECT_CERTIFICATE_FOR_URLS_FILTER_EXAMPLE" /> ระบบจะเลือกเฉพาะใบรับรองไคลเอ็นต์ซึ่งออกโดยใบรับรองที่ใช้ CommonName <ph name="ISSUER_CN_PLACEHOLDER" />
+
+      * เมื่อ <ph name="FILTER_PLACEHOLDER" /> มีทั้งส่วน <ph name="ISSUER_STRING_VALUE" /> และ <ph name="SUBJECT_STRING_VALUE" /> ระบบจะเลือกเฉพาะใบรับรองไคลเอ็นต์ที่เป็นไปตามเงื่อนไขทั้ง 2 ข้อ
+
+      * เมื่อ <ph name="FILTER_PLACEHOLDER" /> มีส่วน <ph name="SUBJECT_STRING_VALUE" /> ที่มีค่า <ph name="FILTER_STRING_ORGANIZATION" /> ใบรับรองต้องมีอย่างน้อย 1 องค์กรที่ตรงกับค่าที่ระบุจึงจะได้รับเลือก
+
+      * เมื่อ <ph name="FILTER_PLACEHOLDER" /> มีส่วน <ph name="SUBJECT_STRING_VALUE" /> ที่มีค่า <ph name="FILTER_STRING_ORGANIZATIONAL_UNIT" /> ใบรับรองต้องมีหน่วยขององค์กรอย่างน้อย 1 หน่วยที่ตรงกับค่าที่ระบุจึงจะได้รับเลือก
+
+      * เมื่อตั้งค่า <ph name="FILTER_PLACEHOLDER" /> เป็น <ph name="EMPTY_DICTIONARY" /> การเลือกใบรับรองไคลเอ็นต์จะไม่มีข้อจำกัดเพิ่มเติม โปรดทราบว่าตัวกรองที่ได้มาจากเว็บเซิร์ฟเวอร์จะยังคงมีผลอยู่
+
+      การไม่ตั้งค่านโยบายนี้หมายความว่าจะไม่มีการเลือกอัตโนมัติสำหรับเว็บไซต์ใดก็ตาม</translation>
 <translation id="5633871703004128675">เปิดใช้ฟีเจอร์การช่วยเหลือพิเศษสำหรับการไฮไลต์เคอร์เซอร์ข้อความ</translation>
 <translation id="5634032995857968056">เปิดใช้แซนด์บ็อกซ์ของคอนเทนเนอร์แอปตัวแสดงผล</translation>
 <translation id="5638334542697444045">ไม่อนุญาตให้ผู้ใช้ใช้การเชื่อมต่อ Wi-Fi ฮอตสปอตจากมือถือโดยอัตโนมัติ</translation>
diff --git a/components/policy/resources/policy_templates_vi.xtb b/components/policy/resources/policy_templates_vi.xtb
index d1237f9..4ba0929 100644
--- a/components/policy/resources/policy_templates_vi.xtb
+++ b/components/policy/resources/policy_templates_vi.xtb
@@ -8028,6 +8028,14 @@
 sẽ gặp sự cố và chỉ sử dụng độ phân giải vị trí dựa trên <ph name="IP" /> trên màn hình <ph name="LOG_IN" />.</translation>
 <translation id="6338982178236723271">Báo cáo thông tin về hệ thống</translation>
 <translation id="6339355882150329269">Không coi người dùng là không hoạt động khi video đang phát</translation>
+<translation id="6347110299837147365">Chính sách này cho phép quản trị viên mã hoá bộ nhớ đệm http của trình duyệt trên ổ đĩa.
+Hiện tại, chính sách này chỉ được hỗ trợ trên Windows.
+
+Khi bạn Bật chính sách này, bộ nhớ đệm của trình duyệt sẽ được mã hoá.
+
+Khi bạn Tắt hoặc không đặt chính sách này, bộ nhớ đệm của trình duyệt sẽ không được mã hoá.
+
+Việc mã hoá bộ nhớ đệm của trình duyệt có thể ảnh hưởng đến hiệu suất.</translation>
 <translation id="6352714113109004581">Nếu đặt chính sách này, thì bạn có thể liệt kê các mẫu URL chỉ định những trang web được phép yêu cầu người dùng cấp quyền truy cập vào thiết bị HID.
 
       Khi bạn không đặt chính sách này, <ph name="DEFAULT_WEB_HID_GUARD_SETTING_POLICY_NAME" /> (nếu được đặt) sẽ áp dụng cho mọi trang web. Nếu không, các chế độ cài đặt cá nhân của người dùng sẽ được áp dụng.
diff --git a/components/policy/resources/templates/policy_definitions/GenerativeAI/GeminiActOnWebSettings.yaml b/components/policy/resources/templates/policy_definitions/GenerativeAI/GeminiActOnWebSettings.yaml
index 249e9b2..322c1948 100644
--- a/components/policy/resources/templates/policy_definitions/GenerativeAI/GeminiActOnWebSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/GenerativeAI/GeminiActOnWebSettings.yaml
@@ -6,7 +6,7 @@
 
   1 = Gemini app is not allowed to take action on the web pages.
 
-  If unset, its behavior is determined by GeminiSettings policy.
+  This policy has no effect when the Gemini app is disabled. For example, the Gemini app can be disabled by <ph name="GEMINI_SETTINGS_POLICY_NAME">GeminiSettings</ph> policy.
 
   For more information, please see https://support.google.com/chrome/a/answer/16291696.
 
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/SystemFeaturesDisableList.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/SystemFeaturesDisableList.yaml
index 2e9b6ad1..24f51287 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/SystemFeaturesDisableList.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/SystemFeaturesDisableList.yaml
@@ -98,6 +98,9 @@
 - caption: Text Editor (supported since version 136)
   name: text_editor
   value: text_editor
+- caption: Vids (supported since version 143)
+  name: vids
+  value: vids
 owners:
 - file://components/policy/OWNERS
 - ayaelattar@chromium.org
@@ -130,6 +133,7 @@
     - google_maps
     - calculator
     - text_editor
+    - vids
     type: string
   type: array
 supported_on:
diff --git a/components/resources/android/page_info_resource_id.h b/components/resources/android/page_info_resource_id.h
index b3e5199..febb087 100644
--- a/components/resources/android/page_info_resource_id.h
+++ b/components/resources/android/page_info_resource_id.h
@@ -29,8 +29,6 @@
 DECLARE_RESOURCE_ID(IDR_PAGEINFO_GOOD_NEW, R.drawable.lock)
 // Bad:
 DECLARE_RESOURCE_ID(IDR_PAGEINFO_BAD, R.drawable.omnibox_not_secure_warning)
-// Internal page:
-DECLARE_RESOURCE_ID(IDR_PAGEINFO_INTERNAL, R.drawable.omnibox_info)
 
 // PageInfoUI colors, used in ConnectionInfoView
 // Good:
diff --git a/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc b/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
index 543a642..36627e6 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
+++ b/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
@@ -753,6 +753,12 @@
     HasHarmfulAppsResponseCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
+  if (harmful_apps_result_for_testing_.has_value()) {
+    auto result_for_test = harmful_apps_result_for_testing_.value();
+    std::move(callback).Run(result_for_test.first, result_for_test.second);
+    return;
+  }
+
   JNIEnv* env = AttachCurrentThread();
   if (!Java_SafeBrowsingApiBridge_ensureSafetyNetApiInitialized(env)) {
     std::move(callback).Run(HasHarmfulAppsResultStatus::FAILED, 0);
diff --git a/components/safe_browsing/android/safe_browsing_api_handler_bridge.h b/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
index 236586b..0645ec5 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
+++ b/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
@@ -98,6 +98,11 @@
     verify_apps_enabled_for_testing_ = result;
   }
 
+  void SetHarmfulAppsResultForTesting(HasHarmfulAppsResultStatus result,
+                                      int num_of_apps) {
+    harmful_apps_result_for_testing_ = std::make_pair(result, num_of_apps);
+  }
+
   // Resets the cached value and callback subscriptions list.
   void ResetSafetyNetIdForTesting();
 
@@ -155,6 +160,9 @@
   std::optional<VerifyAppsEnabledResult> verify_apps_enabled_for_testing_ =
       std::nullopt;
 
+  std::optional<std::pair<HasHarmfulAppsResultStatus, int>>
+      harmful_apps_result_for_testing_ = std::nullopt;
+
   // Set of URLs specified at the command-line to be enforced on as phishing.
   std::set<GURL> artificially_marked_phishing_urls_;
 };
diff --git a/components/safe_browsing/content/browser/client_side_phishing_model.cc b/components/safe_browsing/content/browser/client_side_phishing_model.cc
index 82bda50..10f6da7a 100644
--- a/components/safe_browsing/content/browser/client_side_phishing_model.cc
+++ b/components/safe_browsing/content/browser/client_side_phishing_model.cc
@@ -161,7 +161,7 @@
       beginning_time_(base::TimeTicks::Now()) {
   opt_guide_->AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OPTIMIZATION_TARGET_CLIENT_SIDE_PHISHING,
-      /*model_metadata=*/std::nullopt, this);
+      /*model_metadata=*/std::nullopt, background_task_runner_, this);
 }
 
 void ClientSidePhishingModel::OnModelUpdated(
@@ -245,7 +245,7 @@
     opt_guide_->AddObserverForOptimizationTargetModel(
         optimization_guide::proto::
             OPTIMIZATION_TARGET_CLIENT_SIDE_PHISHING_IMAGE_EMBEDDER,
-        /*model_metadata=*/std::nullopt, this);
+        /*model_metadata=*/std::nullopt, background_task_runner_, this);
   }
 }
 
diff --git a/components/safe_browsing/content/browser/client_side_phishing_model_unittest.cc b/components/safe_browsing/content/browser/client_side_phishing_model_unittest.cc
index a8d75ad6..a359868 100644
--- a/components/safe_browsing/content/browser/client_side_phishing_model_unittest.cc
+++ b/components/safe_browsing/content/browser/client_side_phishing_model_unittest.cc
@@ -54,6 +54,7 @@
   void AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OptimizationTarget optimization_target,
       const std::optional<optimization_guide::proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       optimization_guide::OptimizationTargetModelObserver* observer) override {
     if (optimization_target ==
         optimization_guide::proto::OPTIMIZATION_TARGET_CLIENT_SIDE_PHISHING) {
diff --git a/components/safe_browsing/content/browser/notification_content_detection/test_model_observer_tracker.cc b/components/safe_browsing/content/browser/notification_content_detection/test_model_observer_tracker.cc
index 34964c0..e8d87bc 100644
--- a/components/safe_browsing/content/browser/notification_content_detection/test_model_observer_tracker.cc
+++ b/components/safe_browsing/content/browser/notification_content_detection/test_model_observer_tracker.cc
@@ -12,6 +12,7 @@
 void TestModelObserverTracker::AddObserverForOptimizationTargetModel(
     optimization_guide::proto::OptimizationTarget target,
     const std::optional<optimization_guide::proto::Any>& model_metadata,
+    scoped_refptr<base::SequencedTaskRunner> model_task_runner,
     optimization_guide::OptimizationTargetModelObserver* observer) {
   registered_model_metadata_.insert_or_assign(target, model_metadata);
 }
diff --git a/components/safe_browsing/content/browser/notification_content_detection/test_model_observer_tracker.h b/components/safe_browsing/content/browser/notification_content_detection/test_model_observer_tracker.h
index 4b73c61..9d6a1f38 100644
--- a/components/safe_browsing/content/browser/notification_content_detection/test_model_observer_tracker.h
+++ b/components/safe_browsing/content/browser/notification_content_detection/test_model_observer_tracker.h
@@ -19,6 +19,7 @@
   void AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OptimizationTarget target,
       const std::optional<optimization_guide::proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       optimization_guide::OptimizationTargetModelObserver* observer) override;
 
   bool DidRegisterForTarget(
diff --git a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h
index 7c9cd1d..5dbf388 100644
--- a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h
+++ b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h
@@ -54,10 +54,10 @@
   void operator&(std::ostream&) {}
 };
 
-#define CRSBLOG                                                \
-  (!::safe_browsing::WebUIContentInfoSingleton::HasListener()) \
-      ? static_cast<void>(0)                                   \
-      : ::safe_browsing::CrSBLogVoidify() &                    \
+#define CRSBLOG                                                               \
+  (!::safe_browsing::WebUIContentInfoSingleton::GetInstance()->HasListener()) \
+      ? static_cast<void>(0)                                                  \
+      : ::safe_browsing::CrSBLogVoidify() &                                   \
             ::safe_browsing::CrSBLogMessage().stream()
 
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/content/browser/web_ui/web_ui_content_info_singleton.cc b/components/safe_browsing/content/browser/web_ui/web_ui_content_info_singleton.cc
index c3afbd0..1d4c092 100644
--- a/components/safe_browsing/content/browser/web_ui/web_ui_content_info_singleton.cc
+++ b/components/safe_browsing/content/browser/web_ui/web_ui_content_info_singleton.cc
@@ -26,10 +26,8 @@
   return instance.get();
 }
 
-// static
 bool WebUIContentInfoSingleton::HasListener() {
-  return GetInstance()->has_test_listener_ ||
-         !GetInstance()->webui_instances_.empty();
+  return has_test_listener_ || !webui_instances_.empty();
 }
 
 void WebUIContentInfoSingleton::AddToDownloadUrlsChecked(
diff --git a/components/safe_browsing/content/browser/web_ui/web_ui_content_info_singleton.h b/components/safe_browsing/content/browser/web_ui/web_ui_content_info_singleton.h
index f1839f6..fca9d10fd4 100644
--- a/components/safe_browsing/content/browser/web_ui/web_ui_content_info_singleton.h
+++ b/components/safe_browsing/content/browser/web_ui/web_ui_content_info_singleton.h
@@ -42,7 +42,7 @@
       delete;
 
   // Returns true when there is a listening chrome://safe-browsing tab.
-  static bool HasListener();
+  bool HasListener();
 
   // Add the new message in |download_urls_checked_| and send it to all
   // the open chrome://safe-browsing tabs.
diff --git a/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc b/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc
index 982f218..7fb9a43 100644
--- a/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc
+++ b/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc
@@ -73,6 +73,14 @@
   pref_change_registrar_.RemoveAll();
 }
 
+// static
+void SafeBrowsingMetricsCollector::
+    LogSafeBrowsingNotificationRevocationSourceHistogram(
+        NotificationRevocationSource source) {
+  base::UmaHistogramEnumeration("SafeBrowsing.NotificationRevocationSource",
+                                source);
+}
+
 void SafeBrowsingMetricsCollector::StartLogging() {
   base::TimeDelta log_interval = base::Days(kMetricsLoggingIntervalDay);
   base::Time last_log_time =
diff --git a/components/safe_browsing/core/browser/safe_browsing_metrics_collector.h b/components/safe_browsing/core/browser/safe_browsing_metrics_collector.h
index 5a798e6..3748fa1f 100644
--- a/components/safe_browsing/core/browser/safe_browsing_metrics_collector.h
+++ b/components/safe_browsing/core/browser/safe_browsing_metrics_collector.h
@@ -27,7 +27,12 @@
 enum class NotificationRevocationSource {
   kSocialEngineeringBlocklist = 0,
   kManualSafeBrowsingRevocation = 1,
-  kMaxValue = kManualSafeBrowsingRevocation,
+  kStandardOneTapUnsubscribe = 2,
+  kSuspiciousWarningOneTapUnsubscribe = 3,
+  kDisruptiveAutoRevocation = 4,
+  kUserManuallyChangedSiteSetting = 5,
+  kUnknown = 6,
+  kMaxValue = kUnknown,
 };
 // LINT.ThenChange(//tools/metrics/histograms/metadata/safe_browsing/enums.xml:NotificationRevocationSource)
 
@@ -126,6 +131,11 @@
 
   ~SafeBrowsingMetricsCollector() override = default;
 
+  // Log the histogram that shows the revocation source when notification
+  // permissions are removed.
+  static void LogSafeBrowsingNotificationRevocationSourceHistogram(
+      NotificationRevocationSource source);
+
   // Checks the last logging time. If the time is longer than a day ago, log
   // immediately. Otherwise, schedule the next logging with delay.
   void StartLogging();
diff --git a/components/safe_browsing/core/common/safe_browsing_settings_metrics.h b/components/safe_browsing/core/common/safe_browsing_settings_metrics.h
index c71f31f..bc45239 100644
--- a/components/safe_browsing/core/common/safe_browsing_settings_metrics.h
+++ b/components/safe_browsing/core/common/safe_browsing_settings_metrics.h
@@ -25,7 +25,9 @@
   kSecurityInterstitial = 4,
   // From UX shown due to the Tailored Security setting changing.
   kTailoredSecurity = 5,
-  kMaxValue = kTailoredSecurity
+  // From Tips Notifications bottom sheet promo.
+  kTipsNotificationsPromo = 6,
+  kMaxValue = kTipsNotificationsPromo
 };
 
 // Enum representing actions taken by users visiting the
diff --git a/components/safety_check/BUILD.gn b/components/safety_check/BUILD.gn
index 8035dbe8..bc3cf71d 100644
--- a/components/safety_check/BUILD.gn
+++ b/components/safety_check/BUILD.gn
@@ -4,6 +4,21 @@
 
 import("//build/config/features.gni")
 
+source_set("pref_names") {
+  sources = [ "safety_check_pref_names.h" ]
+}
+
+source_set("prefs") {
+  sources = [
+    "safety_check_prefs.cc",
+    "safety_check_prefs.h",
+  ]
+  deps = [
+    ":pref_names",
+    "//components/prefs",
+  ]
+}
+
 source_set("safety_check") {
   sources = [
     "safety_check.cc",
diff --git a/components/safety_check/safety_check_pref_names.h b/components/safety_check/safety_check_pref_names.h
new file mode 100644
index 0000000..7deef148
--- /dev/null
+++ b/components/safety_check/safety_check_pref_names.h
@@ -0,0 +1,16 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFETY_CHECK_SAFETY_CHECK_PREF_NAMES_H_
+#define COMPONENTS_SAFETY_CHECK_SAFETY_CHECK_PREF_NAMES_H_
+
+namespace safety_check::prefs {
+
+// The pref that stores if the Safety Check Home Module is enabled.
+inline constexpr char kSafetyCheckHomeModuleEnabled[] =
+    "home.module.safety_check.enabled";
+
+}  // namespace safety_check::prefs
+
+#endif  // COMPONENTS_SAFETY_CHECK_SAFETY_CHECK_PREF_NAMES_H_
diff --git a/components/safety_check/safety_check_prefs.cc b/components/safety_check/safety_check_prefs.cc
new file mode 100644
index 0000000..5ae4cf6
--- /dev/null
+++ b/components/safety_check/safety_check_prefs.cc
@@ -0,0 +1,16 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safety_check/safety_check_prefs.h"
+
+#include "components/prefs/pref_registry_simple.h"
+#include "components/safety_check/safety_check_pref_names.h"
+
+namespace safety_check::prefs {
+
+void RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(kSafetyCheckHomeModuleEnabled, true);
+}
+
+}  // namespace safety_check::prefs
diff --git a/components/safety_check/safety_check_prefs.h b/components/safety_check/safety_check_prefs.h
new file mode 100644
index 0000000..5f2263b1
--- /dev/null
+++ b/components/safety_check/safety_check_prefs.h
@@ -0,0 +1,17 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFETY_CHECK_SAFETY_CHECK_PREFS_H_
+#define COMPONENTS_SAFETY_CHECK_SAFETY_CHECK_PREFS_H_
+
+class PrefRegistrySimple;
+
+namespace safety_check::prefs {
+
+// Registers the Profile prefs needed by Safety Check.
+void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+}  // namespace safety_check::prefs
+
+#endif  // COMPONENTS_SAFETY_CHECK_SAFETY_CHECK_PREFS_H_
diff --git a/components/search_engines/util.cc b/components/search_engines/util.cc
index 52e85b8..33ab9dc 100644
--- a/components/search_engines/util.cc
+++ b/components/search_engines/util.cc
@@ -21,6 +21,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "components/country_codes/country_codes.h"
+#include "components/google/core/common/google_util.h"
 #include "components/lens/lens_overlay_mime_type.h"
 #include "components/prefs/pref_service.h"
 #include "components/regional_capabilities/regional_capabilities_utils.h"
@@ -49,6 +50,7 @@
 constexpr char kVisualInputTypeQueryParameterWebpageValue[] = "wp";
 constexpr char kQuerySubmissionTimeQueryParameter[] = "qsubts";
 constexpr char kClientUploadDurationQueryParameter[] = "cud";
+constexpr char kAimUDM[] = "50";
 
 // Computes whether updates to the search engines database are needed.
 //
@@ -615,6 +617,15 @@
   return std::ranges::find(*urls, url, &std::unique_ptr<TemplateURL>::get);
 }
 
+bool IsAimURL(const GURL& url) {
+  if (!google_util::IsGoogleSearchUrl(url)) {
+    return false;
+  }
+  std::string udm;
+  bool has_udm = net::GetValueForKeyInQuery(url, "udm", &udm);
+  return has_udm && udm == kAimUDM;
+}
+
 GURL GetUrlForAim(TemplateURLService* turl_service,
                   omnibox::ChromeAimEntryPoint aim_entrypoint,
                   const base::Time& query_start_time,
@@ -632,7 +643,7 @@
                                                     param.second);
   }
   // This param triggers AI mode as opposed to traditional search.
-  result_url = net::AppendOrReplaceQueryParameter(result_url, "udm", "50");
+  result_url = net::AppendOrReplaceQueryParameter(result_url, "udm", kAimUDM);
   // Don't override the aep param from `additional_params`. This value could be
   // given alongside the match from the server. This should keep precedence
   // over the generic entrypoint value.
diff --git a/components/search_engines/util.h b/components/search_engines/util.h
index 8b5a53c..b54d19c 100644
--- a/components/search_engines/util.h
+++ b/components/search_engines/util.h
@@ -221,6 +221,9 @@
     TemplateURLService::OwnedTemplateURLVector* urls,
     const TemplateURL* url);
 
+// Returns whether the provided `url` leads to the AIM web page.
+bool IsAimURL(const GURL& url);
+
 // Retrieves the URL for the AIM web page.
 // `aim_entrypoint` (aep) is required as it identifies the source of the
 // request. `query_start_time` is the time that the user clicked the submit
diff --git a/components/segmentation_platform/embedder/default_model/tips_notifications_ranker.cc b/components/segmentation_platform/embedder/default_model/tips_notifications_ranker.cc
index 9ee39ebc..f7d8ebd 100644
--- a/components/segmentation_platform/embedder/default_model/tips_notifications_ranker.cc
+++ b/components/segmentation_platform/embedder/default_model/tips_notifications_ranker.cc
@@ -38,18 +38,14 @@
      {TipsNotificationsRanker::kBottomOmniboxTipIdx, kBottomOmnibox}};
 
 // Enum values for histograms.
-constexpr std::array<int32_t, 1> kEnumValueForEnhancedSafeBrowsingUsage{
-    /*EnhancedSafeBrowsing=*/1};
-
 constexpr std::array<int32_t, 1> kEnumValueForQuickDeleteMagicStackImpression{
     /*QuickDelete=*/9};
 
 constexpr FeaturePair<TipsNotificationsRanker::Feature>
     kTipsNotificationsRankerFeatures[] = {
         {TipsNotificationsRanker::kEnhancedSafeBrowsingUseCountIdx,
-         features::UMAEnum("SafeBrowsing.Settings.UserAction.Default",
-                           28,
-                           kEnumValueForEnhancedSafeBrowsingUsage)},
+         features::UserAction("SafeBrowsing.Settings.EnhancedProtectionClicked",
+                              28)},
         {TipsNotificationsRanker::kQuickDeleteMagicStackShownCountIdx,
          features::UMAEnum("MagicStack.Clank.NewTabPage.Module.TopImpressionV2",
                            28,
diff --git a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc
index f8d636d8..154016ff 100644
--- a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc
+++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc
@@ -34,6 +34,7 @@
   void AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OptimizationTarget target,
       const std::optional<optimization_guide::proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       optimization_guide::OptimizationTargetModelObserver* observer) override {
     registered_model_observers_.insert_or_assign(
         target, std::make_pair(model_metadata, observer));
diff --git a/components/signin/public/base/signin_feature_map.cc b/components/signin/public/base/signin_feature_map.cc
index e21489b..14f3c1c 100644
--- a/components/signin/public/base/signin_feature_map.cc
+++ b/components/signin/public/base/signin_feature_map.cc
@@ -24,7 +24,6 @@
     &switches::kForceStartupSigninPromo,
     &switches::kForceHistoryOptInScreen,
     &switches::kSkipCheckForAccountManagementOnSignin,
-    &switches::kUnoForAuto,
     &switches::kSyncEnableBookmarksInTransportMode,
     &switches::kHistoryOptInEducationalTip,
     &switches::kMakeAccountsAvailableInIdentityManager,
diff --git a/components/signin/public/base/signin_switches.cc b/components/signin/public/base/signin_switches.cc
index 7d6f1aff0..2ed0551 100644
--- a/components/signin/public/base/signin_switches.cc
+++ b/components/signin/public/base/signin_switches.cc
@@ -398,10 +398,6 @@
 #endif
 );
 
-#if BUILDFLAG(IS_ANDROID)
-BASE_FEATURE(kUnoForAuto, base::FEATURE_ENABLED_BY_DEFAULT);
-#endif  // BUILDFLAG(IS_ANDROID)
-
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 // When enabled, Chrome will always use the /IssueToken endpoint to fetch access
 // tokens, no matter if a refresh token is bound or not.
diff --git a/components/signin/public/base/signin_switches.h b/components/signin/public/base/signin_switches.h
index 98b4fc19..0511c243 100644
--- a/components/signin/public/base/signin_switches.h
+++ b/components/signin/public/base/signin_switches.h
@@ -338,11 +338,6 @@
 COMPONENT_EXPORT(SIGNIN_SWITCHES)
 BASE_DECLARE_FEATURE(kSyncEnableBookmarksInTransportMode);
 
-#if BUILDFLAG(IS_ANDROID)
-COMPONENT_EXPORT(SIGNIN_SWITCHES)
-BASE_DECLARE_FEATURE(kUnoForAuto);
-#endif  // BUILDFLAG(IS_ANDROID)
-
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 COMPONENT_EXPORT(SIGNIN_SWITCHES)
 BASE_DECLARE_FEATURE(kUseIssueTokenToFetchAccessTokens);
diff --git a/components/strings/components_strings_ar.xtb b/components/strings/components_strings_ar.xtb
index ddffc22..1bc4c91 100644
--- a/components/strings/components_strings_ar.xtb
+++ b/components/strings/components_strings_ar.xtb
@@ -1237,6 +1237,7 @@
 <translation id="305162504811187366">‏سجلّ الوصول إلى الكمبيوتر المكتبي عن بُعد من Chrome، بما في ذلك الطوابع الزمنية والمضيفين وأرقام تعريف جلسات العملاء</translation>
 <translation id="3052868890529250114">‏يمكنك عادةً الاتصال بالمواقع الإلكترونية بشكل آمن، ولكن تعذَّر على Chrome استخدام اتصال آمن في الوقت الحالي. قد يحاول أحد المهاجمين التجسس على اتصالك بالشبكة أو تعديله. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات حول هذا التحذير<ph name="END_LEARN_MORE_LINK" /></translation>
 <translation id="3052964831964880138">انخفاض في سعر <ph name="PRODUCT_NAME" /> بقيمة <ph name="PRICE_DROP" /></translation>
+<translation id="3056660797732519147">‏تم فتح شاشة خطأ عملية "الدفع الآجل" (BNPL). لوحة المفاتيح مخفية.</translation>
 <translation id="3061707000357573562">خدمة رمز التصحيح</translation>
 <translation id="3062655045399308513">حذف بيانات التصفّح…</translation>
 <translation id="3063453392496098461">‏تعذَّر تثبيت خدمات VPN التي تُشغَّل في الخلفية.</translation>
@@ -1991,6 +1992,7 @@
 <translation id="4242981939686657948">للبحث في سجلّ التصفّح بفاعلية أكبر، فعِّل</translation>
 <translation id="4244926541863471678">فيلم</translation>
 <translation id="4246517972543675653">الدراسة في الخارج</translation>
+<translation id="4250343410682686342">‏تم فتح شاشة خطأ عملية "الدفع الآجل" (BNPL) بطول الشاشة.</translation>
 <translation id="4250431568374086873">إن اتصالك بهذا الموقع غير آمن تمامًا</translation>
 <translation id="4250680216510889253">لا</translation>
 <translation id="4250716950689692560">A4x4</translation>
@@ -2507,6 +2509,7 @@
 <translation id="5122786942953798871">ظرف (قطني)</translation>
 <translation id="5123063207673082822">نهاية الأسبوع</translation>
 <translation id="5123433949759960244">كرة السلة</translation>
+<translation id="5124797626438336458">‏تم فتح شاشة خطأ عملية "الدفع الآجل" (BNPL) في نصف الشاشة.</translation>
 <translation id="5125575917473813637">تعديل بطاقة الهوية</translation>
 <translation id="5125751979347152379">‏عنوان URL غير صالح.</translation>
 <translation id="512592033764059484">كرة قدم</translation>
@@ -4175,6 +4178,7 @@
 <translation id="7860345425589240791">‏أدخِل تاريخ انتهاء صلاحية البطاقة الجديد ورمز التحقق من البطاقة (CVC) المتوفّر على <ph name="SIDE_OF_CARD" />.</translation>
 <translation id="7862185352068345852">هل تريد مغادرة الموقع؟</translation>
 <translation id="7865448901209910068">أفضل سرعة</translation>
+<translation id="7865482168955081228">‏تم إغلاق شاشة خطأ عملية "الدفع الآجل" (BNPL).</translation>
 <translation id="7870281855125116701">يتوفّر خصم على المنتج.</translation>
 <translation id="7871445724586827387">‏تغيير كلمة مرور حسابك على Google</translation>
 <translation id="787226752901403048">‏يُرجى تسجيل الدخول إلى Chrome لمشاركة مجموعات علامات التبويب والانضمام إليها والعمل معًا على الأفكار</translation>
diff --git a/components/strings/components_strings_as.xtb b/components/strings/components_strings_as.xtb
index 757c976..73783219 100644
--- a/components/strings/components_strings_as.xtb
+++ b/components/strings/components_strings_as.xtb
@@ -1237,6 +1237,7 @@
 <translation id="305162504811187366">টাইমষ্টাম্প, হ’ষ্ট আৰু ক্লায়েণ্টৰ ছেশ্বন আইডিকে ধৰি Chrome ৰিম’ট ডেস্কটপৰ ইতিহাস</translation>
 <translation id="3052868890529250114">আপুনি সচৰাচৰ ছাইটসমূহৰ সৈতে সুৰক্ষিতভাৱে সংযোগ কৰে, কিন্তু Chromeএ এইবাৰ এটা সুৰক্ষিত সংযোগ ব্যৱহাৰ কৰিব নোৱাৰিলে। কোনো আক্ৰমণকাৰীয়ে আপোনাৰ নেটৱৰ্ক সংযোগত গোপনে নজৰ ৰাখিবলৈ বা সেয়া সংশোধন কৰিবলৈ চেষ্টা কৰি থাকিব পাৰে। <ph name="BEGIN_LEARN_MORE_LINK" />এই সকীয়নিৰ বিষয়ে অধিক জানক<ph name="END_LEARN_MORE_LINK" /></translation>
 <translation id="3052964831964880138"><ph name="PRODUCT_NAME" />ৰ দাম <ph name="PRICE_DROP" /> কমিছে</translation>
+<translation id="3056660797732519147">এতিয়া ক্ৰয় কৰি পাছত পৰিশোধ কৰকৰ আসোঁৱাহ দেখুওৱা স্ক্ৰীন। কীব’ৰ্ড লুকুওৱা হৈছে।</translation>
 <translation id="3061707000357573562">পেচ্চ্‌ সেৱা</translation>
 <translation id="3062655045399308513">ব্ৰাউজিঙৰ ডেটা মচক...</translation>
 <translation id="3063453392496098461">VPNৰ পৃষ্ঠভূমিৰ সেৱা ইনষ্টল কৰিব পৰা নগ’ল।</translation>
@@ -1987,6 +1988,7 @@
 <translation id="4242981939686657948">আপোনাৰ ব্ৰাউজিঙৰ ইতিহাস সন্ধান কৰাৰ অধিক শক্তিশালী উপায়ৰ বাবে, অন কৰক</translation>
 <translation id="4244926541863471678">ফিল্ম</translation>
 <translation id="4246517972543675653">বিদেশত অধ্যয়ন</translation>
+<translation id="4250343410682686342">এতিয়া ক্ৰয় কৰি পাছত পৰিশোধ কৰকৰ আসোঁৱাহ দেখুওৱা স্ক্ৰীন সম্পূৰ্ণ উচ্চতাত খোলা হৈছে।</translation>
 <translation id="4250431568374086873">এই ছাইটটোত আপোনাৰ সংযোগ সম্পূর্ণভাৱে সুৰক্ষিত নহয়</translation>
 <translation id="4250680216510889253">নহয়</translation>
 <translation id="4250716950689692560">A4x4</translation>
@@ -2503,6 +2505,7 @@
 <translation id="5122786942953798871">এনভেল’প (কপাহী)</translation>
 <translation id="5123063207673082822">সপ্তাহৰ অন্ত</translation>
 <translation id="5123433949759960244">বাস্কেটবল</translation>
+<translation id="5124797626438336458">এতিয়া ক্ৰয় কৰি পাছত পৰিশোধ কৰকৰ আসোঁৱাহ দেখুওৱা স্ক্ৰীন অৰ্ধ উচ্চতাত খোলা হৈছে।</translation>
 <translation id="5125575917473813637">পৰিচয় পত্ৰ সম্পাদনা কৰক</translation>
 <translation id="5125751979347152379">অমান্য URL।</translation>
 <translation id="512592033764059484">ফুটবল</translation>
@@ -4169,6 +4172,7 @@
 <translation id="7860345425589240791"><ph name="SIDE_OF_CARD" />ত আপোনাৰ নতুন ম্যাদ উকলাৰ তাৰিখটো আৰু CVC দিয়ক</translation>
 <translation id="7862185352068345852">ছাইটোৰ পৰা বাহিৰ হ’বনে?</translation>
 <translation id="7865448901209910068">উত্তম বেগ</translation>
+<translation id="7865482168955081228">এতিয়া ক্ৰয় কৰি পাছত পৰিশোধ কৰকৰ আসোঁৱাহ দেখুওৱা স্ক্ৰীন বন্ধ কৰা হৈছে।</translation>
 <translation id="7870281855125116701">ডিছকাউণ্ট বিচাৰি পোৱা গৈছে</translation>
 <translation id="7871445724586827387">আপোনাৰ Google একাউণ্টৰ পাছৱৰ্ডটো সলনি কৰক</translation>
 <translation id="787226752901403048">টেবৰ গোটসমূহত শ্বেয়াৰ কৰিবলৈ আৰু যোগদান কৰিবলৈ Chromeত ছাইন ইন কৰক আৰু ধাৰণাসমূহত একেলগে কাম কৰক</translation>
diff --git a/components/strings/components_strings_fa.xtb b/components/strings/components_strings_fa.xtb
index 6050bce4..61c91acf 100644
--- a/components/strings/components_strings_fa.xtb
+++ b/components/strings/components_strings_fa.xtb
@@ -1237,6 +1237,7 @@
 <translation id="305162504811187366">‏سابقه «رایانه ازدور Chrome»، ازجمله مُهرهای زمان، میزبان‌ها، و شناسه‌های جلسه کارخواه</translation>
 <translation id="3052868890529250114">‏معمولاً ازطریق اتصال ایمن به سایت‌ها متصل می‌شوید، اما این بار Chrome نتوانست از اتصال ایمن استفاده کند. ممکن است مهاجمی درحال تلاش برای استراق سمع یا تغییر اتصال شبکه شما باشد. <ph name="BEGIN_LEARN_MORE_LINK" />درباره این هشدار بیشتر بدانید<ph name="END_LEARN_MORE_LINK" /></translation>
 <translation id="3052964831964880138"><ph name="PRICE_DROP" /> کاهش قیمت <ph name="PRODUCT_NAME" /></translation>
+<translation id="3056660797732519147">صفحه خطای «حالا خرید کنید، بعداً بپردازید». صفحه‌کلید پنهان است.</translation>
 <translation id="3061707000357573562">سرویس وصله</translation>
 <translation id="3062655045399308513">حذف کردن داده‌های مرور…</translation>
 <translation id="3063453392496098461">سرویس‌های پس‌زمینه‌ای وی‌پی‌ان نصب نشدند.</translation>
@@ -1991,6 +1992,7 @@
 <translation id="4242981939686657948">برای اینکه سابقه مرور را به‌روشی قدرتمندتر جستجو کنید، این گزینه را روشن کنید:</translation>
 <translation id="4244926541863471678">فیلم</translation>
 <translation id="4246517972543675653">تحصیل در خارج</translation>
+<translation id="4250343410682686342">صفحه خطای «حالا خرید کنید، بعداً بپردازید» به‌صورت کامل باز شده است.</translation>
 <translation id="4250431568374086873">اتصال شما به این سایت کاملاً امن نیست</translation>
 <translation id="4250680216510889253">نه</translation>
 <translation id="4250716950689692560">A4x4</translation>
@@ -2507,6 +2509,7 @@
 <translation id="5122786942953798871">پاکت (پنبه‌ای)</translation>
 <translation id="5123063207673082822">آخر هفته</translation>
 <translation id="5123433949759960244">بسکتبال</translation>
+<translation id="5124797626438336458">صفحه خطای «حالا خرید کنید، بعداً بپردازید» به‌صورت نیمه باز شده است.</translation>
 <translation id="5125575917473813637">ویرایش کردن کارت شناسایی</translation>
 <translation id="5125751979347152379">نشانی وب نامعتبر است.</translation>
 <translation id="512592033764059484">فوتبال</translation>
@@ -4175,6 +4178,7 @@
 <translation id="7860345425589240791">‏تاریخ انقضای جدید و کد تأیید کارت (CVC) را از <ph name="SIDE_OF_CARD" /> وارد کنید</translation>
 <translation id="7862185352068345852">سایت را ترک می‌کنید؟</translation>
 <translation id="7865448901209910068">بهترین سرعت</translation>
+<translation id="7865482168955081228">صفحه خطای «حالا خرید کنید، بعداً بپردازید» بسته شد.</translation>
 <translation id="7870281855125116701">تخفیف پیدا شد</translation>
 <translation id="7871445724586827387">‏تغییر گذرواژه «حساب Google»</translation>
 <translation id="787226752901403048">‏به سیستم Chrome وارد شوید و گروه‌های زبانه را هم‌سانی کنید، به آن‌ها بپیوندید، و با هم روی ایده‌ها کار کنید</translation>
diff --git a/components/strings/components_strings_iw.xtb b/components/strings/components_strings_iw.xtb
index 627fc8b6..25ad196 100644
--- a/components/strings/components_strings_iw.xtb
+++ b/components/strings/components_strings_iw.xtb
@@ -814,6 +814,7 @@
 <translation id="2321291402658100099">ניהול המידע</translation>
 <translation id="2322254345061973671">{COUNT,plural, =1{חלון אחד}one{‫# חלונות}two{‫# חלונות}other{‫# חלונות}}</translation>
 <translation id="232390861414237658">‏אפשר למלא באופן אוטומטי את פרטי הכרטיס הזה לביצוע רכישות ב-Chrome כששומרים אותו לשימוש ב-Google Pay</translation>
+<translation id="2326283484705860548">‏עכשיו אפשר למלא אוטומטית מידע נוסף מ-Google Wallet</translation>
 <translation id="2328651992442742497">יש הרשאה (ברירת מחדל)</translation>
 <translation id="2328955282645810595">כלי בנייה וכלי עבודה ממונעים</translation>
 <translation id="2330137317877982892"><ph name="CREDIT_CARD" />, בתוקף עד <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -824,6 +825,7 @@
 <translation id="2346319942568447007">תמונה שהעתקת</translation>
 <translation id="2347764072711098973">‏אפשר לשלם בתשלומים באמצעות Affirm,‏ Zip או Klarna</translation>
 <translation id="2348509731468399833">מעקב המחירים הופסק</translation>
+<translation id="2348836501965732644">אפשר לנהל את המידע או להשבית את ההצעות האלו בהגדרות</translation>
 <translation id="2349957959687031096">הכפתור לפתיחת כרטיסייה פרטית, מפעילים אותו כדי לפתוח כרטיסייה פרטית חדשה ולגלוש באופן פרטי</translation>
 <translation id="2350796302381711542">האם לאפשר ל-<ph name="HANDLER_HOSTNAME" /> לפתוח את כל קישורי <ph name="PROTOCOL" /> במקום את <ph name="REPLACED_HANDLER_TITLE" />?</translation>
 <translation id="2351718832234620840">‏השרת הזה לא הצליח להוכיח שהוא <ph name="DOMAIN" /> כי הוא משתמש באישור אבטחה שלא נחשב לאמין לפי Chromium או לפי הגדרות המערכת שלך להצפנת תעבורת אינטרנט. יכול להיות שהסיבה לכך היא הגדרה שגויה או שתוקף מיירט את החיבור שלך.</translation>
@@ -1453,6 +1455,7 @@
 <translation id="340924996148642843">אין גישה להשוואה כרגע.</translation>
 <translation id="3409896703495473338">ניהול הגדרות האבטחה</translation>
 <translation id="3411091017011773343">העלאה בעוד <ph name="HOURS" /> שע'</translation>
+<translation id="341134412449102894">הפרטים שלך נשמרו ב-<ph name="IDS_AUTOFILL_GOOGLE_WALLET_TITLE" /> בחשבון <ph name="ACCOUNT" /></translation>
 <translation id="3413466783792948876">‏אם משתמשים ברשת ציבורית, מומלץ להיכנס לאתר אחר כך כדי לשמור על הבטיחות. הסיכון נמוך יותר ברשת מהימנה, כמו ה-Wi-Fi בבית או במקום העבודה.</translation>
 <translation id="3414275587143250384">מחשוב מבוזר ומחשוב ענן</translation>
 <translation id="3414952576877147120">גודל:</translation>
@@ -2015,7 +2018,7 @@
 <translation id="4277529130885813215">שימוש במכשיר אחר</translation>
 <translation id="4277937682389409325">כתובת מקומית</translation>
 <translation id="4278090321534187713">הכרטיסים שהמוכר הזה לא מקבל מושבתים</translation>
-<translation id="4278390842282768270">מותר</translation>
+<translation id="4278390842282768270">יש אישור</translation>
 <translation id="4281245629646759298">צהוב בהיר</translation>
 <translation id="4281998142035485137">{0,plural, =1{לפתוח את הקובץ הסודי?}one{לפתוח את הקבצים הסודיים?}two{לפתוח את הקבצים הסודיים?}other{לפתוח את הקבצים הסודיים?}}</translation>
 <translation id="4282280603030594840">קניית רכב</translation>
diff --git a/components/strings/components_strings_ja.xtb b/components/strings/components_strings_ja.xtb
index 18ed0824..8804269b 100644
--- a/components/strings/components_strings_ja.xtb
+++ b/components/strings/components_strings_ja.xtb
@@ -3887,7 +3887,7 @@
       &lt;/ol&gt;</translation>
 <translation id="7408613996403626141">おもちゃ</translation>
 <translation id="7410852728357935715">デバイスにキャスト</translation>
-<translation id="741204030948306876">ON にする</translation>
+<translation id="741204030948306876">オンにする</translation>
 <translation id="7416351320495623771">パスワードを管理…</translation>
 <translation id="7416898721136759658">奨学金、補助金</translation>
 <translation id="741772962178517671">Affirm</translation>
diff --git a/components/strings/components_strings_ka.xtb b/components/strings/components_strings_ka.xtb
index 94fd510..03bb812 100644
--- a/components/strings/components_strings_ka.xtb
+++ b/components/strings/components_strings_ka.xtb
@@ -1237,6 +1237,7 @@
 <translation id="305162504811187366">Chrome-ის დისტანციური სამუშაო დაფის ისტორია, მათ შორის დროის ანაბეჭდები, ჰოსტები და კლიენტთა სესიების ID-ები</translation>
 <translation id="3052868890529250114">საიტებს, როგორც წესი, დაცულად უკავშირდებით, თუმცა Chrome-მა ამჯერად ვერ დაამყარა უსაფრთხო კავშირი. თავდამსხმელს შეეძლება თქვენს ქსელის კავშირზე თვალთვალი ან მისი შეცვლა. <ph name="BEGIN_LEARN_MORE_LINK" />შეიტყვეთ მეტი ამ გაფრთხილების შესახებ<ph name="END_LEARN_MORE_LINK" /></translation>
 <translation id="3052964831964880138"><ph name="PRODUCT_NAME" />-ზე ფასის <ph name="PRICE_DROP" />-ით შემცირება</translation>
+<translation id="3056660797732519147">„იყიდეთ ახლა, გადაიხადეთ მოგვიანებით“ შეცდომის ეკრანი. კლავიატურა დამალულია.</translation>
 <translation id="3061707000357573562">ჩასწორების სერვისი</translation>
 <translation id="3062655045399308513">დათვალიერების მონაცემების წაშლა...</translation>
 <translation id="3063453392496098461">VPN-ის ფონური სერვისების ინსტალაცია წარუმატებელია.</translation>
@@ -1991,6 +1992,7 @@
 <translation id="4242981939686657948">დათვალიერების ისტორიაში უკეთ ძიებისთვის ჩართეთ</translation>
 <translation id="4244926541863471678">აფსკი</translation>
 <translation id="4246517972543675653">სწავლა საზღვარგარეთ</translation>
+<translation id="4250343410682686342">„იყიდეთ ახლა, გადაიხადეთ მოგვიანებით“ შეცდომის ეკრანი გახსნილია სრულ სიმაღლეზე.</translation>
 <translation id="4250431568374086873">თქვენი კავშირი ამ საიტთან სრულად დაცული არ არის</translation>
 <translation id="4250680216510889253">არა</translation>
 <translation id="4250716950689692560">A4x4</translation>
@@ -2507,6 +2509,7 @@
 <translation id="5122786942953798871">კონვერტი (ბამბის)</translation>
 <translation id="5123063207673082822">შაბათ-კვირა</translation>
 <translation id="5123433949759960244">კალათბურთის ბურთი</translation>
+<translation id="5124797626438336458">„იყიდეთ ახლა, გადაიხადეთ მოგვიანებით“ შეცდომის ეკრანი გახსნილია ნახევარ სიმაღლეზე.</translation>
 <translation id="5125575917473813637">პირადობის მოწმობის რედაქტირება</translation>
 <translation id="5125751979347152379">არასწორი URL.</translation>
 <translation id="512592033764059484">ფეხბურთი</translation>
@@ -4174,6 +4177,7 @@
 <translation id="7860345425589240791">თავიდან შეიყვანეთ მოქმედების ვადის გასვლის თარიღი და CVC, რომლებიც მოცემულია <ph name="SIDE_OF_CARD" /></translation>
 <translation id="7862185352068345852">გსურთ საიტიდან გასვლა?</translation>
 <translation id="7865448901209910068">საუკეთესო სიჩქარე</translation>
+<translation id="7865482168955081228">„იყიდეთ ახლა, გადაიხადეთ მოგვიანებით“ შეცდომის ეკრანი დახურულია.</translation>
 <translation id="7870281855125116701">მოიძებნა ფასდაკლება</translation>
 <translation id="7871445724586827387">თქვენი Google ანგარიშის პაროლის შეცვლა</translation>
 <translation id="787226752901403048">შედით Chrome-ში, რომ გააზიაროთ ჩანართების ჯგუფები, შეუერთდეთ მათ და ერთად იმუშაოთ იდეებზე</translation>
diff --git a/components/strings/components_strings_km.xtb b/components/strings/components_strings_km.xtb
index 0f36c3f..f1210b7 100644
--- a/components/strings/components_strings_km.xtb
+++ b/components/strings/components_strings_km.xtb
@@ -816,6 +816,7 @@
 <translation id="2321291402658100099">គ្រប់គ្រង​ព័ត៌មាន</translation>
 <translation id="2322254345061973671">{COUNT,plural, =1{វិនដូ 1}other{វិនដូ #}}</translation>
 <translation id="232390861414237658">បំពេញ​កាតនេះ​ដោយ​ស្វ័យប្រវត្តិ​សម្រាប់​ការទិញ​ដែលបាន​ធ្វើឡើង​នៅក្នុង Chrome នៅពេល​ដែលវា​ត្រូវបាន​រក្សាទុក ដើម្បីប្រើ​ជាមួយ Google Pay</translation>
+<translation id="2326283484705860548">ឥឡូវនេះ អ្នកអាច​បំពេញ​ព័ត៌មាន​កាន់តែច្រើន​ដោយ​ស្វ័យប្រវត្តិ​ពី Google Wallet បានហើយ</translation>
 <translation id="2328651992442742497">អនុញ្ញាត (លំនាំដើម)</translation>
 <translation id="2328955282645810595">ឧបករណ៍​ប្រើថាមពល និងសាងសង់</translation>
 <translation id="2330137317877982892"><ph name="CREDIT_CARD" /> ផុតកំណត់នៅថ្ងៃទី <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -826,6 +827,7 @@
 <translation id="2346319942568447007">រូបភាព​ដែលអ្នក​បានចម្លង</translation>
 <translation id="2347764072711098973">ប្រើ Affirm, Zip ឬ Klarna ដើម្បី​ទូទាត់​ជាបន្តបន្ទាប់</translation>
 <translation id="2348509731468399833">បាន​បញ្ឈប់​ការ​តាម​ដាន​តម្លៃ។</translation>
+<translation id="2348836501965732644">គ្រប់គ្រង​ព័ត៌មាន​របស់អ្នក ឬ​បិទ​ការណែនាំ​ទាំងនេះ​នៅក្នុង "ការកំណត់"</translation>
 <translation id="2349957959687031096">ប៊ូតុង "បើកផ្ទាំងឯកជន", បើកដំណើរការ ដើម្បីបើកផ្ទាំងឯកជនថ្មីសម្រាប់រុករកជាលក្ខណៈឯកជន</translation>
 <translation id="2350796302381711542">អនុញ្ញាតឲ្យ <ph name="HANDLER_HOSTNAME" /> បើក <ph name="PROTOCOL" /> តំណទាំងអស់ជំនួសឲ្យ <ph name="REPLACED_HANDLER_TITLE" />?</translation>
 <translation id="2351718832234620840">ម៉ាស៊ីនមេ​នេះ​មិនអាច​បញ្ជាក់ថា​វាជា <ph name="DOMAIN" /> បានទេ ដោយសារ​វាកំពុងប្រើ​វិញ្ញាបនបត្រ​សុវត្ថិភាព​ដែល​មិនត្រូវបាន​ទុកចិត្តដោយ Chromium ឬការកំណត់រចនាសម្ព័ន្ធ​ប្រព័ន្ធ​របស់អ្នក​សម្រាប់​ការ​អ៊ីនគ្រីប​ចរាចរណ៍​បណ្ដាញ​ឡើយ។ បញ្ហានេះ​អាច​បណ្ដាល​មកពី​ការកំណត់​រចនាសម្ព័ន្ធ​មិនត្រឹមត្រូវ ឬ​អ្នកវាយប្រហារ​ជ្រៀតជ្រែក​ការតភ្ជាប់​របស់អ្នក។</translation>
diff --git a/components/strings/components_strings_lo.xtb b/components/strings/components_strings_lo.xtb
index d5fa4cf..95192f1 100644
--- a/components/strings/components_strings_lo.xtb
+++ b/components/strings/components_strings_lo.xtb
@@ -814,6 +814,7 @@
 <translation id="2321291402658100099">ຈັດການຂໍ້ມູນ</translation>
 <translation id="2322254345061973671">{COUNT,plural, =1{1 ໜ້າຈໍ}other{# ໜ້າຈໍ}}</translation>
 <translation id="232390861414237658">ຕື່ມຂໍ້ມູນບັດນີ້ອັດຕະໂນມັດສຳລັບການຊື້ທີ່ດຳເນີນການໃນ Chrome ເມື່ອບັນທຶກໄວ້ເພື່ອໃຊ້ກັບ Google Pay</translation>
+<translation id="2326283484705860548">ຕອນນີ້ທ່ານສາມາດຕື່ມຂໍ້ມູນເພີ່ມເຕີມຈາກ Google Wallet ໂດຍອັດຕະໂນມັດໄດ້ແລ້ວ</translation>
 <translation id="2328651992442742497">ອະນຸຍາດ (ຄ່າເລີ່ມຕົ້ນ)</translation>
 <translation id="2328955282645810595">ເຄື່ອງມືກໍ່ສ້າງ ແລະ ພະລັງງານ</translation>
 <translation id="2330137317877982892"><ph name="CREDIT_CARD" />, ໝົດອາຍຸໃນວັນທີ <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -824,6 +825,7 @@
 <translation id="2346319942568447007">ຮູບພາບທີ່ທ່ານສຳເນົາ</translation>
 <translation id="2347764072711098973">ໃຊ້ Affirm, Zip ຫຼື Klarna ເພື່ອຈ່າຍເປັນງວດ</translation>
 <translation id="2348509731468399833">ຢຸດການຕິດຕາມລາຄາແລ້ວ.</translation>
+<translation id="2348836501965732644">ຈັດການຂໍ້ມູນຂອງທ່ານ ຫຼື ປິດການແນະນຳເຫຼົ່ານີ້ໄດ້ໃນການຕັ້ງຄ່າ</translation>
 <translation id="2349957959687031096">ປຸ່ມເປີດແຖບບໍ່ເປີດເຜີຍຕົວຕົນ, ເປີດນຳໃຊ້ເພື່ອເປີດແຖບບໍ່ເປີດເຜີຍຕົວຕົນໃໝ່ໃນການທ່ອງເວັບແບບສ່ວນຕົວ</translation>
 <translation id="2350796302381711542">ອະນຸຍາດໃຫ້ <ph name="HANDLER_HOSTNAME" /> ເປີດທຸກ <ph name="PROTOCOL" /> ລິ້ງແທນ <ph name="REPLACED_HANDLER_TITLE" /> ບໍ?</translation>
 <translation id="2351718832234620840">ເຊີບເວີນີ້ບໍ່ສາມາດຢືນຢັນໄດ້ວ່າມັນແມ່ນ <ph name="DOMAIN" /> ເນື່ອງຈາກມັນກຳລັງໃຊ້ໃບຮັບຮອງຄວາມປອດໄພທີ່ Chromium ຫຼື ການຕັ້ງຄ່າລະບົບຂອງທ່ານສຳລັບການເຂົ້າລະຫັດທຣາບຟິກເວັບບໍ່ເຊື່ອຖື. ນີ້ອາດເກີດຈາກການຕັ້ງຄ່າຜິດ ຫຼື ມີຜູ້ໂຈມຕີສະກັດກັ້ນການເຊື່ອມຕໍ່ຂອງທ່ານ.</translation>
diff --git a/components/strings/components_strings_mn.xtb b/components/strings/components_strings_mn.xtb
index 3980c1b..b95b0ff 100644
--- a/components/strings/components_strings_mn.xtb
+++ b/components/strings/components_strings_mn.xtb
@@ -1237,6 +1237,7 @@
 <translation id="305162504811187366">Хугацаа тэмдэглэгч, хост болон клиентийн харилцан үйлдлийн ID зэргийг багтаасан Chrome Remote Desktop-н түүх</translation>
 <translation id="3052868890529250114">Та сайтуудад ихэвчлэн аюулгүй холбогддог ч Chrome энэ удаад аюулгүй холболт ашиглаж чадсангүй. Халдагч таны сүлжээний холболтыг чагнах эсвэл өөрчлөхөөр оролдож байж магадгүй. <ph name="BEGIN_LEARN_MORE_LINK" />Энэ сануулгын талаар нэмэлт мэдээлэл авах<ph name="END_LEARN_MORE_LINK" /></translation>
 <translation id="3052964831964880138"><ph name="PRODUCT_NAME" />-н үнэ <ph name="PRICE_DROP" />-р буурсан</translation>
+<translation id="3056660797732519147">"Одоо ав, дараа төл" төлбөрийн хэрэгслийн алдааны дэлгэц. Гарыг нуусан.</translation>
 <translation id="3061707000357573562">Patch үйлчилгээ</translation>
 <translation id="3062655045399308513">Интернэтээр үзсэн өгөгдлийг устгах...</translation>
 <translation id="3063453392496098461">VPN-н дэвсгэрийн үйлчилгээг суулгаж чадсангүй.</translation>
@@ -1992,6 +1993,7 @@
 <translation id="4242981939686657948">Хөтчийн түүхээсээ хайх илүү хүчирхэг аргыг авах бол дараахыг асаана уу:</translation>
 <translation id="4244926541863471678">Хальс</translation>
 <translation id="4246517972543675653">Гадаадад сурах</translation>
+<translation id="4250343410682686342">"Одоо ав, дараа төл" төлбөрийн хэрэгслийн алдааны дэлгэцийг бүтэн өндрөөр нээсэн.</translation>
 <translation id="4250431568374086873">Энэ сайтын холболт аюултай байж болзошгүй</translation>
 <translation id="4250680216510889253">Үгүй</translation>
 <translation id="4250716950689692560">A4x4</translation>
@@ -2508,6 +2510,7 @@
 <translation id="5122786942953798871">Дугтуй (хөвөн)</translation>
 <translation id="5123063207673082822">Амралтын өдөр</translation>
 <translation id="5123433949759960244">Сагсан бөмбөг</translation>
+<translation id="5124797626438336458">"Одоо ав, дараа төл" төлбөрийн хэрэгслийн алдааны дэлгэцийг тал өндрөөр нээсэн.</translation>
 <translation id="5125575917473813637">Иргэний үнэмлэхийг засна уу</translation>
 <translation id="5125751979347152379">Хүчингүй холбоос</translation>
 <translation id="512592033764059484">Хөл бөмбөг</translation>
@@ -4174,6 +4177,7 @@
 <translation id="7860345425589240791"><ph name="SIDE_OF_CARD" /> дээрх шинэ хүчинтэй хугацаа болон Карт баталгаажуулалтын кодоо оруулна уу</translation>
 <translation id="7862185352068345852">Сайтыг орхих уу?</translation>
 <translation id="7865448901209910068">Хамгийн оновчтой хурд</translation>
+<translation id="7865482168955081228">"Одоо ав, дараа төл" төлбөрийн хэрэгслийн алдааны дэлгэцийг хаасан.</translation>
 <translation id="7870281855125116701">Хөнгөлөлт олдлоо</translation>
 <translation id="7871445724586827387">Google Бүртгэлийнхээ нууц үгийг өөрчлөөрэй</translation>
 <translation id="787226752901403048">Табын бүлэг хуваалцах, нэгдэхийн тулд Chrome-д нэвтэрч, санаан дээр хамтран ажиллаарай</translation>
diff --git a/components/strings/components_strings_ms.xtb b/components/strings/components_strings_ms.xtb
index aed45a95..5390404e 100644
--- a/components/strings/components_strings_ms.xtb
+++ b/components/strings/components_strings_ms.xtb
@@ -1455,6 +1455,7 @@
 <translation id="340924996148642843">Perbandingan tidak tersedia sekarang</translation>
 <translation id="3409896703495473338">Urus tetapan keselamatan</translation>
 <translation id="3411091017011773343">Muat naik selepas <ph name="HOURS" /> jam</translation>
+<translation id="341134412449102894">Maklumat anda disimpan pada <ph name="IDS_AUTOFILL_GOOGLE_WALLET_TITLE" /> untuk <ph name="ACCOUNT" /></translation>
 <translation id="3413466783792948876">Anda akan lebih selamat jika melawati laman ini kemudian jika anda menggunakan rangkaian awam. Terdapat kurang risiko daripada rangkaian yang dipercayai, seperti Wi-Fi rumah atau tempat kerja anda.</translation>
 <translation id="3414275587143250384">Pengkomputeran Awan &amp; Teragih</translation>
 <translation id="3414952576877147120">Saiz:</translation>
diff --git a/components/strings/components_strings_th.xtb b/components/strings/components_strings_th.xtb
index e6a6897..fc4840f1 100644
--- a/components/strings/components_strings_th.xtb
+++ b/components/strings/components_strings_th.xtb
@@ -814,6 +814,7 @@
 <translation id="2321291402658100099">จัดการข้อมูล</translation>
 <translation id="2322254345061973671">{COUNT,plural, =1{1 หน้าต่าง}other{# หน้าต่าง}}</translation>
 <translation id="232390861414237658">ป้อนข้อมูลบัตรนี้โดยอัตโนมัติสำหรับการซื้อใน Chrome เมื่อบันทึกบัตรไว้ใช้กับ Google Pay</translation>
+<translation id="2326283484705860548">ตอนนี้คุณสามารถป้อนข้อมูลเพิ่มเติมจาก Google Wallet โดยอัตโนมัติได้แล้ว</translation>
 <translation id="2328651992442742497">อนุญาต (ค่าเริ่มต้น)</translation>
 <translation id="2328955282645810595">การก่อสร้างและเครื่องมือไฟฟ้า</translation>
 <translation id="2330137317877982892"><ph name="CREDIT_CARD" /> หมดอายุ <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -824,6 +825,7 @@
 <translation id="2346319942568447007">รูปภาพที่คุณคัดลอก</translation>
 <translation id="2347764072711098973">ใช้ Affirm, Zip หรือ Klarna เพื่อแบ่งจ่าย</translation>
 <translation id="2348509731468399833">หยุดติดตามราคาแล้ว</translation>
+<translation id="2348836501965732644">จัดการข้อมูลหรือปิดการแนะนำเหล่านี้ได้ในการตั้งค่า</translation>
 <translation id="2349957959687031096">ปุ่มเปิดแท็บที่ไม่ระบุตัวตน เปิดใช้งานเพื่อเปิดแท็บใหม่ที่ไม่ระบุตัวตนในการท่องเว็บแบบส่วนตัว</translation>
 <translation id="2350796302381711542">ต้องการอนุญาตให้ <ph name="HANDLER_HOSTNAME" /> เปิดลิงก์ <ph name="PROTOCOL" /> ทั้งหมดแทน <ph name="REPLACED_HANDLER_TITLE" /> ไหม</translation>
 <translation id="2351718832234620840">เซิร์ฟเวอร์นี้พิสูจน์ไม่ได้ว่าเป็น <ph name="DOMAIN" /> เนื่องจากใช้ใบรับรองความปลอดภัยที่ Chromium หรือการกำหนดค่าระบบของคุณไม่เชื่อถือสำหรับการเข้ารหัสการเข้าชมเว็บ สาเหตุอาจเกิดจากการกำหนดค่าผิดหรือผู้โจมตีขัดขวางการเชื่อมต่อของคุณ</translation>
diff --git a/components/strings/components_strings_ur.xtb b/components/strings/components_strings_ur.xtb
index 12e7a3d4..0203048a 100644
--- a/components/strings/components_strings_ur.xtb
+++ b/components/strings/components_strings_ur.xtb
@@ -1453,6 +1453,7 @@
 <translation id="340924996148642843">موازنہ ابھی دستیاب نہیں ہے</translation>
 <translation id="3409896703495473338">سیکیورٹی کی ترتیبات کا نظم کریں</translation>
 <translation id="3411091017011773343"><ph name="HOURS" /> گھنٹے میں اپ لوڈ کریں</translation>
+<translation id="341134412449102894">آپ کی معلومات <ph name="ACCOUNT" /> کے <ph name="IDS_AUTOFILL_GOOGLE_WALLET_TITLE" /> پر محفوظ ہو گئی ہے</translation>
 <translation id="3413466783792948876">‏اگر آپ کوئی عوامی نیٹ ورک استعمال کر رہے ہیں تو اس سائٹ کو بعد میں ملاحظہ کرنا محفوظ ترین ہے۔ قابل اعتماد نیٹ ورک، جیسے آپ کے گھر یا دفتر کے Wi-Fi سے کم خطرہ لاحق ہوتا ہے۔</translation>
 <translation id="3414275587143250384">تقسیم کردہ اور کلاؤڈ کمپیوٹنگ</translation>
 <translation id="3414952576877147120">سائز:</translation>
diff --git a/components/strings/components_strings_vi.xtb b/components/strings/components_strings_vi.xtb
index 619793bb..3b79801 100644
--- a/components/strings/components_strings_vi.xtb
+++ b/components/strings/components_strings_vi.xtb
@@ -1237,6 +1237,7 @@
 <translation id="305162504811187366">Nhật ký Chrome Remote Desktop, bao gồm cả dấu thời gian, mã phiên ứng dụng và máy chủ</translation>
 <translation id="3052868890529250114">Bạn thường kết nối an toàn với các trang web nhưng lần này Chrome không thể sử dụng kết nối bảo mật. Kẻ tấn công có thể đang tìm cách nghe trộm hoặc sửa đổi kết nối mạng của bạn. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm về cảnh báo này<ph name="END_LEARN_MORE_LINK" /></translation>
 <translation id="3052964831964880138">Giảm giá <ph name="PRICE_DROP" /> đối với <ph name="PRODUCT_NAME" /></translation>
+<translation id="3056660797732519147">Màn hình báo lỗi khi Mua trước trả sau. Bàn phím đang ẩn.</translation>
 <translation id="3061707000357573562">Dịch vụ vá lỗi</translation>
 <translation id="3062655045399308513">Xoá dữ liệu duyệt web...</translation>
 <translation id="3063453392496098461">Không cài đặt được dịch vụ nền VPN.</translation>
@@ -1991,6 +1992,7 @@
 <translation id="4242981939686657948">Để tìm thông tin trong nhật ký duyệt web theo cách hiệu quả hơn, hãy bật</translation>
 <translation id="4244926541863471678">Phim</translation>
 <translation id="4246517972543675653">Du học</translation>
+<translation id="4250343410682686342">Màn hình báo lỗi khi Mua trước trả sau đã mở toàn bộ.</translation>
 <translation id="4250431568374086873">Kết nối của bạn tới trang web này không đủ an toàn</translation>
 <translation id="4250680216510889253">Không</translation>
 <translation id="4250716950689692560">A4x4</translation>
@@ -2507,6 +2509,7 @@
 <translation id="5122786942953798871">Phong bì (Cotton)</translation>
 <translation id="5123063207673082822">Cuối tuần</translation>
 <translation id="5123433949759960244">Bóng rổ</translation>
+<translation id="5124797626438336458">Màn hình báo lỗi khi Mua trước trả sau đã mở trên một nửa màn hình.</translation>
 <translation id="5125575917473813637">Chỉnh sửa thẻ căn cước</translation>
 <translation id="5125751979347152379">URL không hợp lệ.</translation>
 <translation id="512592033764059484">Bóng đá</translation>
@@ -4175,6 +4178,7 @@
 <translation id="7860345425589240791">Nhập ngày hết hạn mới và CVC trên <ph name="SIDE_OF_CARD" /></translation>
 <translation id="7862185352068345852">Rời khỏi trang web?</translation>
 <translation id="7865448901209910068">Tốc độ tốt nhất</translation>
+<translation id="7865482168955081228">Màn hình báo lỗi khi Mua trước trả sau đã đóng.</translation>
 <translation id="7870281855125116701">Đã tìm thấy ưu đãi giảm giá</translation>
 <translation id="7871445724586827387">Thay đổi mật khẩu cho Tài khoản Google của bạn</translation>
 <translation id="787226752901403048">Đăng nhập vào Chrome để chia sẻ và tham gia nhóm thẻ, cũng như cùng nhau phát triển ý tưởng</translation>
diff --git a/components/strings/components_strings_zh-HK.xtb b/components/strings/components_strings_zh-HK.xtb
index e38dab4..77e6cb0 100644
--- a/components/strings/components_strings_zh-HK.xtb
+++ b/components/strings/components_strings_zh-HK.xtb
@@ -1237,6 +1237,7 @@
 <translation id="305162504811187366">Chrome 遠端桌面記錄,包括時間戳記、主機和用戶端工作階段 ID</translation>
 <translation id="3052868890529250114">你通常可經由安全連線前往網站,但 Chrome 目前無法使用安全連線。攻擊者可能會試圖監視或修改你的網絡連線。<ph name="BEGIN_LEARN_MORE_LINK" />進一步瞭解此警告訊息<ph name="END_LEARN_MORE_LINK" /></translation>
 <translation id="3052964831964880138"><ph name="PRODUCT_NAME" /> 降價 <ph name="PRICE_DROP" /></translation>
+<translation id="3056660797732519147">「先買後付」嘅錯誤畫面。隱藏咗鍵盤。</translation>
 <translation id="3061707000357573562">修補服務</translation>
 <translation id="3062655045399308513">刪除瀏覽資料…</translation>
 <translation id="3063453392496098461">無法安裝 VPN 背景服務。</translation>
@@ -1990,6 +1991,7 @@
 <translation id="4242981939686657948">如要使用進階的瀏覽記錄搜尋方式,請開啟</translation>
 <translation id="4244926541863471678">薄膜</translation>
 <translation id="4246517972543675653">海外升學</translation>
+<translation id="4250343410682686342">「先買後付」嘅錯誤畫面宜家顯示喺成個螢幕。</translation>
 <translation id="4250431568374086873">您與此網站的連線並非完全安全</translation>
 <translation id="4250680216510889253">否</translation>
 <translation id="4250716950689692560">A4x4</translation>
@@ -2506,6 +2508,7 @@
 <translation id="5122786942953798871">信封 (棉紙)</translation>
 <translation id="5123063207673082822">週末</translation>
 <translation id="5123433949759960244">籃球</translation>
+<translation id="5124797626438336458">「先買後付」嘅錯誤畫面宜家顯示喺螢幕下半部。</translation>
 <translation id="5125575917473813637">編輯身分證</translation>
 <translation id="5125751979347152379">網址無效。</translation>
 <translation id="512592033764059484">足球</translation>
@@ -4174,6 +4177,7 @@
 <translation id="7860345425589240791">輸入<ph name="SIDE_OF_CARD" />的新到期日和 CVC</translation>
 <translation id="7862185352068345852">要離開網站嗎?</translation>
 <translation id="7865448901209910068">最佳速度</translation>
+<translation id="7865482168955081228">「先買後付」嘅錯誤畫面閂咗。</translation>
 <translation id="7870281855125116701">已找到折扣</translation>
 <translation id="7871445724586827387">變更 Google 帳戶密碼</translation>
 <translation id="787226752901403048">登入 Chrome 即可分享和加入分頁群組,集思廣益</translation>
diff --git a/components/subresource_filter/FILTER_LIST_GENERATION.md b/components/subresource_filter/FILTER_LIST_GENERATION.md
index ae284a9..b2b8bb3d 100644
--- a/components/subresource_filter/FILTER_LIST_GENERATION.md
+++ b/components/subresource_filter/FILTER_LIST_GENERATION.md
@@ -60,8 +60,14 @@
 ```sh
 1. ninja -C out/Release/ subresource_filter_tools
 2. wget https://easylist.to/easylist/easylist.txt
-3. out/Release/ruleset_converter --input_format=filter-list --output_format=unindexed-ruleset --input_files=easylist.txt --output_file=easylist_unindexed
-4. out/Release/subresource_indexing_tool easylist_unindexed easylist_indexed
+# Convert `||domain.xyz^` rules into `||domain.xyz^$third-party` so
+# that we don't match all of the subresource requests when visiting
+# a top-level frame with that domain as a first party.
+# crbug.com/448915986
+3. awk '{ if (/^\s*!|^\s*@@|^\s*$/) { print; next } if (/^\s*\|\|[^/]*\^\s*$/) { print $0 "$third-party"; next } print }' easylist.txt > easylist_third.txt
+4. mv easylist_third.txt easylist.txt
+5. out/Release/ruleset_converter --input_format=filter-list --output_format=unindexed-ruleset --input_files=easylist.txt --output_file=easylist_unindexed
+6. out/Release/subresource_indexing_tool easylist_unindexed easylist_indexed
 ```
 
 ## 3. Generate the smaller filter list
diff --git a/components/sync/engine/nigori/cross_user_sharing_public_private_key_pair.cc b/components/sync/engine/nigori/cross_user_sharing_public_private_key_pair.cc
index 3980eee..b7ccfeb 100644
--- a/components/sync/engine/nigori/cross_user_sharing_public_private_key_pair.cc
+++ b/components/sync/engine/nigori/cross_user_sharing_public_private_key_pair.cc
@@ -32,21 +32,10 @@
   return CrossUserSharingPublicPrivateKeyPair();
 }
 
-// static
-std::optional<CrossUserSharingPublicPrivateKeyPair>
-CrossUserSharingPublicPrivateKeyPair::CreateByImport(
-    base::span<const uint8_t> private_key) {
-  if (private_key.size() != X25519_PRIVATE_KEY_LEN) {
-    return std::nullopt;
-  }
-  return CrossUserSharingPublicPrivateKeyPair(private_key);
-}
-
 CrossUserSharingPublicPrivateKeyPair::CrossUserSharingPublicPrivateKeyPair(
-    base::span<const uint8_t> private_key) {
-  CHECK_EQ(static_cast<size_t>(X25519_PRIVATE_KEY_LEN), private_key.size());
+    base::span<const uint8_t, X25519_PRIVATE_KEY_LEN> private_key) {
   CHECK(EVP_HPKE_KEY_init(key_.get(), EVP_hpke_x25519_hkdf_sha256(),
-                          private_key.data(), X25519_PRIVATE_KEY_LEN));
+                          private_key.data(), private_key.size()));
 }
 
 CrossUserSharingPublicPrivateKeyPair::CrossUserSharingPublicPrivateKeyPair() {
diff --git a/components/sync/engine/nigori/cross_user_sharing_public_private_key_pair.h b/components/sync/engine/nigori/cross_user_sharing_public_private_key_pair.h
index a1c72a6..b636083 100644
--- a/components/sync/engine/nigori/cross_user_sharing_public_private_key_pair.h
+++ b/components/sync/engine/nigori/cross_user_sharing_public_private_key_pair.h
@@ -22,9 +22,9 @@
  public:
   // Generate a X25519 key pair.
   static CrossUserSharingPublicPrivateKeyPair GenerateNewKeyPair();
-  // Initialize the Public-private key-pair using `private_key`.
-  static std::optional<CrossUserSharingPublicPrivateKeyPair> CreateByImport(
-      base::span<const uint8_t> private_key);
+
+  explicit CrossUserSharingPublicPrivateKeyPair(
+      base::span<const uint8_t, X25519_PRIVATE_KEY_LEN> private_key);
 
   CrossUserSharingPublicPrivateKeyPair(
       const CrossUserSharingPublicPrivateKeyPair& other) = delete;
@@ -63,8 +63,6 @@
 
  private:
   CrossUserSharingPublicPrivateKeyPair();
-  explicit CrossUserSharingPublicPrivateKeyPair(
-      base::span<const uint8_t> private_key);
 
   bssl::ScopedEVP_HPKE_KEY key_;
 };
diff --git a/components/sync/engine/nigori/cross_user_sharing_public_private_key_pair_unittest.cc b/components/sync/engine/nigori/cross_user_sharing_public_private_key_pair_unittest.cc
index 9c75d3e..74fe08e7 100644
--- a/components/sync/engine/nigori/cross_user_sharing_public_private_key_pair_unittest.cc
+++ b/components/sync/engine/nigori/cross_user_sharing_public_private_key_pair_unittest.cc
@@ -63,38 +63,17 @@
 
 TEST(CrossUserSharingPublicPrivateKeyPairTest, CreateByImportShouldSucceed) {
   std::vector<uint8_t> private_key(X25519_PRIVATE_KEY_LEN, 0xDE);
+  std::optional<base::span<uint8_t, X25519_PRIVATE_KEY_LEN>> fixed_key =
+      base::span(private_key).to_fixed_extent<X25519_PRIVATE_KEY_LEN>();
+  ASSERT_TRUE(fixed_key);
 
-  std::optional<CrossUserSharingPublicPrivateKeyPair> key =
-      CrossUserSharingPublicPrivateKeyPair::CreateByImport(private_key);
-
-  ASSERT_TRUE(key.has_value());
-
+  CrossUserSharingPublicPrivateKeyPair key(*fixed_key);
   std::array<uint8_t, X25519_PRIVATE_KEY_LEN> raw_private_key =
-      key->GetRawPrivateKey();
+      key.GetRawPrivateKey();
 
   EXPECT_THAT(private_key, testing::ElementsAreArray(raw_private_key));
 }
 
-TEST(CrossUserSharingPublicPrivateKeyPairTest,
-     CreateByImportShouldFailOnShorterKey) {
-  std::vector<uint8_t> private_key(X25519_PRIVATE_KEY_LEN - 1, 0xDE);
-
-  std::optional<CrossUserSharingPublicPrivateKeyPair> key =
-      CrossUserSharingPublicPrivateKeyPair::CreateByImport(private_key);
-
-  EXPECT_FALSE(key.has_value());
-}
-
-TEST(CrossUserSharingPublicPrivateKeyPairTest,
-     CreateByImportShouldFailOnLongerKey) {
-  std::vector<uint8_t> private_key(X25519_PRIVATE_KEY_LEN + 1, 0xDE);
-
-  std::optional<CrossUserSharingPublicPrivateKeyPair> key =
-      CrossUserSharingPublicPrivateKeyPair::CreateByImport(private_key);
-
-  EXPECT_FALSE(key.has_value());
-}
-
 TEST(CrossUserSharingPublicPrivateKeyPairTest, ShouldEncryptAndDecrypt) {
   CrossUserSharingPublicPrivateKeyPair sender_key_pair =
       CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair();
diff --git a/components/sync/nigori/cross_user_sharing_keys.cc b/components/sync/nigori/cross_user_sharing_keys.cc
index a12017a5..2fd6bfd1 100644
--- a/components/sync/nigori/cross_user_sharing_keys.cc
+++ b/components/sync/nigori/cross_user_sharing_keys.cc
@@ -28,11 +28,7 @@
 
 CrossUserSharingPublicPrivateKeyPair CloneKeyPair(
     const CrossUserSharingPublicPrivateKeyPair& key_pair) {
-  std::optional<CrossUserSharingPublicPrivateKeyPair> clone =
-      CrossUserSharingPublicPrivateKeyPair::CreateByImport(
-          key_pair.GetRawPrivateKey());
-  CHECK(clone.has_value());
-  return std::move(clone.value());
+  return CrossUserSharingPublicPrivateKeyPair(key_pair.GetRawPrivateKey());
 }
 
 }  // namespace
@@ -91,16 +87,13 @@
 
 bool CrossUserSharingKeys::AddKeyPairFromProto(
     const sync_pb::CrossUserSharingPrivateKey& key) {
-  std::vector<uint8_t> private_key(key.x25519_private_key().begin(),
-                                   key.x25519_private_key().end());
-  std::optional<CrossUserSharingPublicPrivateKeyPair> key_pair =
-      CrossUserSharingPublicPrivateKeyPair::CreateByImport(private_key);
-
-  if (!key_pair.has_value()) {
+  std::optional<base::span<const uint8_t, X25519_PRIVATE_KEY_LEN>> fixed_key =
+      base::as_byte_span(key.x25519_private_key())
+          .to_fixed_extent<X25519_PRIVATE_KEY_LEN>();
+  if (!fixed_key) {
     return false;
   }
-
-  SetKeyPair(std::move(key_pair.value()), key.version());
+  SetKeyPair(CrossUserSharingPublicPrivateKeyPair(*fixed_key), key.version());
   return true;
 }
 
diff --git a/components/sync/nigori/pending_local_nigori_commit.cc b/components/sync/nigori/pending_local_nigori_commit.cc
index e53ce01..003dd588 100644
--- a/components/sync/nigori/pending_local_nigori_commit.cc
+++ b/components/sync/nigori/pending_local_nigori_commit.cc
@@ -40,11 +40,9 @@
       CrossUserSharingPublicKey::CreateByImport(
           cross_user_sharing_key_pair.GetRawPublicKey());
   state->cross_user_sharing_key_pair_version = version;
-  std::optional<CrossUserSharingPublicPrivateKeyPair> key_pair =
-      CrossUserSharingPublicPrivateKeyPair::CreateByImport(
-          cross_user_sharing_key_pair.GetRawPrivateKey());
-  CHECK(key_pair.has_value());
-  state->cryptographer->SetKeyPair(std::move(key_pair.value()), version);
+  CrossUserSharingPublicPrivateKeyPair key_pair(
+      cross_user_sharing_key_pair.GetRawPrivateKey());
+  state->cryptographer->SetKeyPair(std::move(key_pair), version);
   state->cryptographer->SelectDefaultCrossUserSharingKey(version);
 }
 
diff --git a/components/sync/service/sync_service_impl_startup_unittest.cc b/components/sync/service/sync_service_impl_startup_unittest.cc
index 4ec7e2a..f102157 100644
--- a/components/sync/service/sync_service_impl_startup_unittest.cc
+++ b/components/sync/service/sync_service_impl_startup_unittest.cc
@@ -39,6 +39,7 @@
   MockSyncServiceObserver() = default;
 
   MOCK_METHOD(void, OnStateChanged, (SyncService*), (override));
+  MOCK_METHOD(void, OnSyncShutdown, (SyncService*), (override));
 };
 
 }  // namespace
diff --git a/components/sync/service/sync_service_impl_unittest.cc b/components/sync/service/sync_service_impl_unittest.cc
index ec32ed1..01a136d 100644
--- a/components/sync/service/sync_service_impl_unittest.cc
+++ b/components/sync/service/sync_service_impl_unittest.cc
@@ -109,6 +109,7 @@
 class MockSyncServiceObserver : public SyncServiceObserver {
  public:
   MOCK_METHOD(void, OnStateChanged, (SyncService * sync), (override));
+  MOCK_METHOD(void, OnSyncShutdown, (SyncService * sync), (override));
 };
 
 class TestSyncServiceObserver : public SyncServiceObserver {
@@ -119,6 +120,7 @@
     setup_in_progress_ = sync->IsSetupInProgress();
     auth_error_ = sync->GetAuthError();
   }
+  void OnSyncShutdown(SyncService* sync) override { NOTREACHED(); }
 
   bool setup_in_progress() const { return setup_in_progress_; }
   GoogleServiceAuthError auth_error() const { return auth_error_; }
diff --git a/components/sync_preferences/common_syncable_prefs_database.cc b/components/sync_preferences/common_syncable_prefs_database.cc
index a17a44f..d9e80ea 100644
--- a/components/sync_preferences/common_syncable_prefs_database.cc
+++ b/components/sync_preferences/common_syncable_prefs_database.cc
@@ -146,6 +146,7 @@
   kCrossDeviceOmniboxIsInBottomPosition = 97,
   kAutofillWasNameAndEmailProfileUsed = 98,
   kCrossDeviceCrossPlatformPromosIOS16thActiveDay = 99,
+  kCrossDeviceSafetyCheckHomeModuleEnabled = 100,
   // See components/sync_preferences/README.md about adding new entries here.
   // vvvvv IMPORTANT! vvvvv
   // Note to the reviewer: IT IS YOUR RESPONSIBILITY to ensure that new syncable
@@ -262,12 +263,16 @@
         {prefs::kCookieControlsMode,
          {syncable_prefs_ids::kCookieControlsMode, syncer::PREFERENCES,
           PrefSensitivity::kNone, MergeBehavior::kNone}},
+        {prefs::kCrossDeviceCrossPlatformPromosIOS16thActiveDay,
+         {syncable_prefs_ids::kCrossDeviceCrossPlatformPromosIOS16thActiveDay,
+          syncer::PREFERENCES, PrefSensitivity::kNone,
+          MergeBehavior::kMergeableDict}},
         {prefs::kCrossDeviceOmniboxIsInBottomPosition,
          {syncable_prefs_ids::kCrossDeviceOmniboxIsInBottomPosition,
           syncer::PREFERENCES, PrefSensitivity::kNone,
           MergeBehavior::kMergeableDict}},
-        {prefs::kCrossDeviceCrossPlatformPromosIOS16thActiveDay,
-         {syncable_prefs_ids::kCrossDeviceCrossPlatformPromosIOS16thActiveDay,
+        {prefs::kCrossDeviceSafetyCheckHomeModuleEnabled,
+         {syncable_prefs_ids::kCrossDeviceSafetyCheckHomeModuleEnabled,
           syncer::PREFERENCES, PrefSensitivity::kNone,
           MergeBehavior::kMergeableDict}},
         {prefs::kSafeBrowsingEnabled,
diff --git a/components/sync_preferences/cross_device_pref_tracker/BUILD.gn b/components/sync_preferences/cross_device_pref_tracker/BUILD.gn
index 4f8b8f1..210b6f7 100644
--- a/components/sync_preferences/cross_device_pref_tracker/BUILD.gn
+++ b/components/sync_preferences/cross_device_pref_tracker/BUILD.gn
@@ -33,6 +33,7 @@
     "//components/keyed_service/core",
     "//components/omnibox/browser:pref_names",
     "//components/prefs",
+    "//components/safety_check:pref_names",
     "//components/sync_device_info",
   ]
 
diff --git a/components/sync_preferences/cross_device_pref_tracker/DEPS b/components/sync_preferences/cross_device_pref_tracker/DEPS
index f586cff1..6aacaea 100644
--- a/components/sync_preferences/cross_device_pref_tracker/DEPS
+++ b/components/sync_preferences/cross_device_pref_tracker/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+components/omnibox/browser/omnibox_pref_names.h",
-]
\ No newline at end of file
+  "+components/safety_check/safety_check_pref_names.h",
+]
diff --git a/components/sync_preferences/cross_device_pref_tracker/OWNERS b/components/sync_preferences/cross_device_pref_tracker/OWNERS
new file mode 100644
index 0000000..1e78033
--- /dev/null
+++ b/components/sync_preferences/cross_device_pref_tracker/OWNERS
@@ -0,0 +1,4 @@
+bwwilliams@google.com
+jhimawan@google.com
+scottyoder@google.com
+treib@chromium.org
diff --git a/components/sync_preferences/cross_device_pref_tracker/common_cross_device_pref_provider.cc b/components/sync_preferences/cross_device_pref_tracker/common_cross_device_pref_provider.cc
index a124fcc..a329b19 100644
--- a/components/sync_preferences/cross_device_pref_tracker/common_cross_device_pref_provider.cc
+++ b/components/sync_preferences/cross_device_pref_tracker/common_cross_device_pref_provider.cc
@@ -6,25 +6,18 @@
 
 #include "base/no_destructor.h"
 #include "components/omnibox/browser/omnibox_pref_names.h"
+#include "components/safety_check/safety_check_pref_names.h"
 
 namespace sync_preferences {
 
-namespace {
-
-// Helper to return a common, static empty set.
-const base::flat_set<std::string_view>& GetEmptySet() {
-  static const base::NoDestructor<base::flat_set<std::string_view>> kEmptySet;
-  return *kEmptySet;
-}
-
-}  // namespace
-
 CommonCrossDevicePrefProvider::CommonCrossDevicePrefProvider() = default;
 CommonCrossDevicePrefProvider::~CommonCrossDevicePrefProvider() = default;
 
 const base::flat_set<std::string_view>&
 CommonCrossDevicePrefProvider::GetProfilePrefs() const {
-  return GetEmptySet();
+  static const base::NoDestructor<base::flat_set<std::string_view>>
+      kProfilePrefs({safety_check::prefs::kSafetyCheckHomeModuleEnabled});
+  return *kProfilePrefs;
 }
 
 // These prefs should be the tracked prefs, not the ones prefixed with
diff --git a/components/sync_preferences/cross_device_pref_tracker/prefs/cross_device_pref_names.h b/components/sync_preferences/cross_device_pref_tracker/prefs/cross_device_pref_names.h
index e0819436..2438241 100644
--- a/components/sync_preferences/cross_device_pref_tracker/prefs/cross_device_pref_names.h
+++ b/components/sync_preferences/cross_device_pref_tracker/prefs/cross_device_pref_names.h
@@ -10,16 +10,24 @@
 // Alphabetical list of preferences used by the cross device pref tracker.
 // Keep alphabetized, and document each.
 
-// Dictionary that stores the value of the omnibox position (bottom/top) across
-// a user's syncing devices.
-inline constexpr char kCrossDeviceOmniboxIsInBottomPosition[] =
-    "cross_device.omnibox.is_in_bottom_position";
+// go/keep-sorted start newline_separated=yes skip_lines=1
 
 // Dictionary that stores the value of the 16th active day for cross-platform
 // promos on iOS across a user's syncing devices.
 inline constexpr char kCrossDeviceCrossPlatformPromosIOS16thActiveDay[] =
     "cross_device.cross_platform_promos.ios_16th_active_day";
 
+// Dictionary that stores the value of the omnibox position (bottom/top) across
+// a user's syncing devices.
+inline constexpr char kCrossDeviceOmniboxIsInBottomPosition[] =
+    "cross_device.omnibox.is_in_bottom_position";
+
+// Dictionary that stores if the Safety Check Home Module is enabled.
+inline constexpr char kCrossDeviceSafetyCheckHomeModuleEnabled[] =
+    "cross_device.home.module.safety_check.enabled";
+
+// go/keep-sorted end
+
 }  // namespace prefs
 
 #endif  // COMPONENTS_SYNC_PREFERENCES_CROSS_DEVICE_PREF_TRACKER_PREFS_CROSS_DEVICE_PREF_NAMES_H_
diff --git a/components/sync_preferences/cross_device_pref_tracker/prefs/cross_device_pref_registry.cc b/components/sync_preferences/cross_device_pref_tracker/prefs/cross_device_pref_registry.cc
index b3ba4fd..cdc168c 100644
--- a/components/sync_preferences/cross_device_pref_tracker/prefs/cross_device_pref_registry.cc
+++ b/components/sync_preferences/cross_device_pref_tracker/prefs/cross_device_pref_registry.cc
@@ -12,10 +12,13 @@
 
 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterDictionaryPref(
+      prefs::kCrossDeviceCrossPlatformPromosIOS16thActiveDay,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+  registry->RegisterDictionaryPref(
       prefs::kCrossDeviceOmniboxIsInBottomPosition,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterDictionaryPref(
-      prefs::kCrossDeviceCrossPlatformPromosIOS16thActiveDay,
+      prefs::kCrossDeviceSafetyCheckHomeModuleEnabled,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
 }
 
diff --git a/components/test/ios/BUILD.gn b/components/test/ios/BUILD.gn
index 2e125e2..6598b30 100644
--- a/components/test/ios/BUILD.gn
+++ b/components/test/ios/BUILD.gn
@@ -6,4 +6,5 @@
   assert(is_ios, "components/test/ios/BUILD.gn only usable for iOS")
   testonly = true
   sources = [ "test_utils.h" ]
+  deps = [ "//third_party/ocmock" ]
 }
diff --git a/components/test/ios/DEPS b/components/test/ios/DEPS
new file mode 100644
index 0000000..f6063e1b
--- /dev/null
+++ b/components/test/ios/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+third_party/ocmock"
+]
diff --git a/components/test/ios/test_utils.h b/components/test/ios/test_utils.h
index 30c03ff..8596944 100644
--- a/components/test/ios/test_utils.h
+++ b/components/test/ios/test_utils.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_TEST_IOS_TEST_UTILS_H_
 #define COMPONENTS_TEST_IOS_TEST_UTILS_H_
 
+#import "third_party/ocmock/OCMock/OCMock.h"
+
 // Expects that the i-th parameter of the invocation is equal to `expected`
 #define andCompareObjectAtIndex(expected, index)                      \
   andDo(^(NSInvocation * invocation) {                                \
@@ -54,4 +56,15 @@
     return YES;                                            \
   }]
 
+namespace ios::OCM {
+
+// Returns a OCMArg that accepts any pointer, and can be used as argument of
+// pointer of type T*.
+template <typename T>
+T* AnyPointer() {
+  return static_cast<T*>([OCMArg anyPointer]);
+}
+
+}  // namespace ios::OCM
+
 #endif  // COMPONENTS_TEST_IOS_TEST_UTILS_H_
diff --git a/components/url_pattern/simple_url_pattern_matcher.cc b/components/url_pattern/simple_url_pattern_matcher.cc
index c0a32fc..14e1714 100644
--- a/components/url_pattern/simple_url_pattern_matcher.cc
+++ b/components/url_pattern/simple_url_pattern_matcher.cc
@@ -82,10 +82,10 @@
   return false;
 }
 
-std::string ResolveRelativePathnamePattern(const GURL& base_url,
+std::string ResolveRelativePathnamePattern(const GURL* base_url,
                                            std::string_view pathname) {
-  if (base_url.IsStandard() && !IsAbsolutePathname(pathname)) {
-    std::string base_path = EscapePatternString(base_url.GetPath());
+  if (base_url && base_url->IsStandard() && !IsAbsolutePathname(pathname)) {
+    std::string base_path = EscapePatternString(base_url->GetPath());
     auto slash_index = base_path.rfind('/');
     if (slash_index != std::string::npos) {
       // Extract the baseURL path up to and including the first slash.  Append
@@ -169,7 +169,7 @@
 // static
 base::expected<std::unique_ptr<SimpleUrlPatternMatcher>, std::string>
 SimpleUrlPatternMatcher::Create(std::string_view constructor_string,
-                                const GURL& base_url) {
+                                const GURL* base_url) {
   std::optional<Component> protocol_component;
   bool protocol_matches_a_special_scheme_flag = false;
   auto pattern_result =
@@ -183,23 +183,17 @@
                                protocol_matches_a_special_scheme_flag);
 }
 
-// static
-base::expected<SimpleUrlPatternMatcher::PatternInit, std::string>
-SimpleUrlPatternMatcher::CreatePatternInit(
+static base::expected<liburlpattern::ConstructorStringParser, std::string>
+ParseConstructorString(
     std::string_view constructor_string,
-    const GURL& base_url,
-    std::optional<Component>* protocol_component_out,
+    std::optional<SimpleUrlPatternMatcher::Component>* protocol_component_out,
     bool* protocol_matches_a_special_scheme_flag_out) {
-  if (!base_url.is_valid()) {
-    return base::unexpected("Invalid base URL");
-  }
-
   // Spec: Set init to the result of running parse a constructor string given
   // input.
   // https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
   liburlpattern::ConstructorStringParser constructor_string_parser(
       constructor_string);
-  std::optional<Component> protocol_component;
+  std::optional<SimpleUrlPatternMatcher::Component> protocol_component;
   bool protocol_matches_a_special_scheme_flag = false;
   absl::Status result = constructor_string_parser.Parse(
       [&protocol_component, &protocol_matches_a_special_scheme_flag](
@@ -207,7 +201,7 @@
           -> base::expected<bool, absl::Status> {
         // Spec: Let protocol component be the result of compiling a component
         // given protocol string, canonicalize a protocol, and default options.
-        auto component_result = Component::Create(
+        auto component_result = SimpleUrlPatternMatcher::Component::Create(
             protocol_string, url_pattern::ProtocolEncodeCallback,
             kDefaultOptions);
         if (!component_result.has_value()) {
@@ -237,8 +231,28 @@
     *protocol_matches_a_special_scheme_flag_out =
         protocol_matches_a_special_scheme_flag;
   }
+  return std::move(constructor_string_parser);
+}
+
+// static
+base::expected<SimpleUrlPatternMatcher::PatternInit, std::string>
+SimpleUrlPatternMatcher::CreatePatternInit(
+    std::string_view constructor_string,
+    const GURL* base_url,
+    std::optional<Component>* protocol_component_out,
+    bool* protocol_matches_a_special_scheme_flag_out) {
+  if (base_url && !base_url->is_valid()) {
+    return base::unexpected("Invalid base URL");
+  }
+
+  auto constructor_string_parser =
+      ParseConstructorString(constructor_string, protocol_component_out,
+                             protocol_matches_a_special_scheme_flag_out);
+  if (!constructor_string_parser.has_value()) {
+    return base::unexpected(constructor_string_parser.error());
+  }
   const liburlpattern::ConstructorStringParser::Result& init =
-      constructor_string_parser.GetResult();
+      constructor_string_parser->GetResult();
 
   // Spec: Let processedInit be the result of process a URLPatternInit given
   // init, "pattern", null, null, null, null, null, null, null, and null.
@@ -246,6 +260,8 @@
   // The following code are running shortcuts of the steps of "process a
   // URLPatternInit".
   // https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
+  // If base_url is required to complete the URLPatternInit according to the
+  // spec but is not given, an error is returned.
   //
   // [protocol]
   // - Spec: If init["protocol"] does not exist, then set result["protocol"] to
@@ -258,9 +274,12 @@
   // Note: "process protocol for init" removes a single trailing ":". But
   //       ConstructorStringParser doesn't set the trailing ":". So we don't
   //       need the logic for the trailing ":" removal.
+  if (!init.protocol && !base_url) {
+    return base::unexpected("Protocol may not be omitted");
+  }
   std::optional<std::string> protocol =
       init.protocol ? std::string(*init.protocol)
-                    : EscapePatternString(base_url.GetScheme());
+                    : EscapePatternString(base_url->GetScheme());
   // [username]
   // - Spec: If type is not "pattern" and init contains none of "protocol",
   //   "hostname", "port" and "username", then set result["username"] to the
@@ -294,11 +313,11 @@
   //   result of process hostname for init given init["hostname"] and type.
   // Note: "process hostname for init" do nothing when type is "pattern".
   std::optional<std::string> hostname =
-      init.hostname
-          ? std::make_optional(std::string(*init.hostname))
-          : (init.protocol
-                 ? std::nullopt
-                 : std::make_optional(EscapePatternString(base_url.GetHost())));
+      init.hostname ? std::make_optional(std::string(*init.hostname))
+                    : (init.protocol || !base_url
+                           ? std::nullopt
+                           : std::make_optional(
+                                 EscapePatternString(base_url->GetHost())));
   // [port]
   // - Spec: If init contains none of "protocol", "hostname", and "port", then:
   //   - If baseURL’s port is null, then set result["port"] to the empty string.
@@ -308,9 +327,9 @@
   // Note: "process port for init" do nothing when type is "pattern".
   std::optional<std::string> port =
       init.port ? std::make_optional(std::string(*init.port))
-                : ((init.protocol || init.hostname)
+                : ((init.protocol || init.hostname || !base_url)
                        ? std::nullopt
-                       : std::make_optional(base_url.GetPort()));
+                       : std::make_optional(base_url->GetPort()));
   // [pathname]
   // - Spec: If init contains none of "protocol", "hostname", "port", and
   //   "pathname", then set result["pathname"] to the result of processing a
@@ -338,13 +357,18 @@
   //     result["pathname"], result["protocol"], and type.
   // Note: The second logic is implemented in ResolveRelativePathnamePattern().
   //       "process pathname for init" do nothing when type is "pattern".
+  if (!base_url && init.pathname && url::IsStandard(*protocol) &&
+      !IsAbsolutePathname(*init.pathname)) {
+    return base::unexpected("Relative pathname not allowed");
+  }
   std::optional<std::string> pathname =
       init.pathname
           ? std::make_optional(
                 ResolveRelativePathnamePattern(base_url, *init.pathname))
-          : ((init.protocol || init.hostname || init.port)
+          : ((init.protocol || init.hostname || init.port || !base_url)
                  ? std::nullopt
-                 : std::make_optional(EscapePatternString(base_url.GetPath())));
+                 : std::make_optional(
+                       EscapePatternString(base_url->GetPath())));
   // [search]
   // - Spec: If init contains none of "protocol", "hostname", "port",
   //   "pathname", and "search", then:
@@ -358,12 +382,12 @@
   //       ConstructorStringParser doesn't set the leading "?". So we don't
   //       need the logic for the leading "?" removal.
   std::optional<std::string> search =
-      init.search
-          ? std::make_optional(std::string(*init.search))
-          : ((init.protocol || init.hostname || init.port || init.pathname)
-                 ? std::nullopt
-                 : std::make_optional(
-                       EscapePatternString(base_url.GetQuery())));
+      init.search ? std::make_optional(std::string(*init.search))
+                  : ((init.protocol || init.hostname || init.port ||
+                      init.pathname || !base_url)
+                         ? std::nullopt
+                         : std::make_optional(
+                               EscapePatternString(base_url->GetQuery())));
   // [hash]
   // - Spec: If init contains none of "protocol", "hostname", "port",
   //   "pathname", "search", and "hash", then:
@@ -380,9 +404,9 @@
       init.hash
           ? std::make_optional(std::string(*init.hash))
           : ((init.protocol || init.hostname || init.port || init.pathname ||
-              init.search)
+              init.search || !base_url)
                  ? std::nullopt
-                 : std::make_optional(EscapePatternString(base_url.GetRef())));
+                 : std::make_optional(EscapePatternString(base_url->GetRef())));
 
   CHECK(protocol);
 
diff --git a/components/url_pattern/simple_url_pattern_matcher.h b/components/url_pattern/simple_url_pattern_matcher.h
index 87476b3..2c0e60a 100644
--- a/components/url_pattern/simple_url_pattern_matcher.h
+++ b/components/url_pattern/simple_url_pattern_matcher.h
@@ -125,9 +125,10 @@
 
   // Creates a SimpleUrlPatternMatcher from a constructor string and a base URL.
   // If the constructor string is invalid or it contains regexp group, this
-  // method returns an error string.
+  // method returns an error string. If `base_url` is nullptr, relative patterns
+  // in `constructor_string` produce an error.
   static base::expected<std::unique_ptr<SimpleUrlPatternMatcher>, std::string>
-  Create(std::string_view constructor_string, const GURL& base_url);
+  Create(std::string_view constructor_string, const GURL* base_url);
 
   SimpleUrlPatternMatcher(Component protocol,
                           Component username,
@@ -151,8 +152,10 @@
   friend class SimpleUrlPatternMatcherTest;
 
   // Creates a `PatternInit` with the result of parsing a constructor string and
-  // apply `base_url`. And returns the result of processing a URLPatternInit,
-  // and processing the default port of the protocol component.
+  // apply `base_url` if given. And returns the result of processing a
+  // URLPatternInit, and processing the default port of the protocol component.
+  // If `base_url` is nullptr, relative or incomplete PatternInits produce an
+  // error.
   // https://urlpattern.spec.whatwg.org/#parse-a-constructor-string
   // https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit
   //
@@ -164,7 +167,7 @@
   // https://urlpattern.spec.whatwg.org/#compute-protocol-matches-a-special-scheme-flag
   static base::expected<PatternInit, std::string> CreatePatternInit(
       std::string_view url_pattern,
-      const GURL& base_url,
+      const GURL* base_url,
       std::optional<Component>* protocol_component_out,
       bool* protocol_matches_a_special_scheme_flag_out);
 
diff --git a/components/url_pattern/simple_url_pattern_matcher_unittest.cc b/components/url_pattern/simple_url_pattern_matcher_unittest.cc
index 58049d55..73728ac 100644
--- a/components/url_pattern/simple_url_pattern_matcher_unittest.cc
+++ b/components/url_pattern/simple_url_pattern_matcher_unittest.cc
@@ -136,14 +136,19 @@
   static base::expected<SimpleUrlPatternMatcher::PatternInit, std::string>
   CreatePatternInit(const std::string_view& url_pattern, const GURL& base_url) {
     return SimpleUrlPatternMatcher::CreatePatternInit(
-        url_pattern, base_url,
+        url_pattern, &base_url,
         /*protocol_matcher_out*=*/nullptr,
         /*should_treat_as_standard_url_out=*/nullptr);
   }
   static base::expected<std::unique_ptr<SimpleUrlPatternMatcher>, std::string>
   CreateMatcher(const std::string_view& constructor_string,
                 const GURL& base_url) {
-    return SimpleUrlPatternMatcher::Create(constructor_string, base_url);
+    return SimpleUrlPatternMatcher::Create(constructor_string, &base_url);
+  }
+
+  static base::expected<std::unique_ptr<SimpleUrlPatternMatcher>, std::string>
+  CreateMatcherWithoutBaseUrl(const std::string_view& constructor_string) {
+    return SimpleUrlPatternMatcher::Create(constructor_string, nullptr);
   }
 };
 
@@ -424,4 +429,169 @@
     }
   }
 }
+
+TEST_F(SimpleUrlPatternMatcherTest, CreateWithoutBaseUrl) {
+  struct {
+    std::string_view constructor_string;
+    std::optional<testing::Matcher<const SimpleUrlPatternMatcher::PatternInit&>>
+        expected_pattern;
+    std::optional<std::string_view> expected_error;
+    std::vector<std::string_view> match_urls;
+    std::vector<std::string_view> non_match_urls;
+  } test_cases[] = {
+
+      // Test cases for SimpleUrlPatternMatcher creation success
+
+      // Absolute URL
+      {.constructor_string = "https://example.com/piyo/fuga",
+       .expected_pattern = ExpectPatternInit(
+           /*protocol=*/"https", /*username=*/std::nullopt,
+           /*password=*/std::nullopt, /*hostname=*/"example.com",
+           /*port=*/"", /*pathname=*/"/piyo/fuga",
+           /*search=*/std::nullopt,
+           /*hash=*/std::nullopt),
+       .match_urls = {"https://example.com/piyo/fuga",
+                      "https://example.com/piyo/fuga?bar"},
+       .non_match_urls = {"https://example.com/foo/",
+                          "https://example.com/foo/piyo/fuga",
+                          "https://example.com/piyo"}},
+
+      // Absolute URL with absolute pathname
+      {.constructor_string = "https://example.com/foo",
+       .expected_pattern = ExpectPatternInit(
+           /*protocol=*/"https", /*username=*/std::nullopt,
+           /*password=*/std::nullopt, /*hostname=*/"example.com",
+           /*port=*/"", /*pathname=*/"/foo",
+           /*search=*/std::nullopt,
+           /*hash=*/std::nullopt),
+       .match_urls = {"https://example.com/foo", "https://example.com/foo?bar"},
+       .non_match_urls = {"https://example.com/bar"}},
+
+      // Absolute URL with different hostname
+      {.constructor_string = "https://example.net/piyo/fuga",
+       .expected_pattern = ExpectPatternInit(
+           /*protocol=*/"https", /*username=*/std::nullopt,
+           /*password=*/std::nullopt, /*hostname=*/"example.net",
+           /*port=*/"", /*pathname=*/"/piyo/fuga",
+           /*search=*/std::nullopt,
+           /*hash=*/std::nullopt),
+       .match_urls = {"https://example.net/piyo/fuga",
+                      "https://example.net/piyo/fuga?bar"},
+       .non_match_urls = {"https://example.com/foo/",
+                          "https://example.com/foo/piyo/fuga"}},
+
+      // Absolute URL with a default port
+      {.constructor_string = "https://example.com:443/piyo/fuga",
+       .expected_pattern = ExpectPatternInit(
+           /*protocol=*/"https", /*username=*/std::nullopt,
+           /*password=*/std::nullopt, /*hostname=*/"example.com",
+           /*port=*/"", /*pathname=*/"/piyo/fuga",
+           /*search=*/std::nullopt,
+           /*hash=*/std::nullopt),
+       .match_urls = {"https://example.com/piyo/fuga",
+                      "https://example.com:443/piyo/fuga"},
+       .non_match_urls = {"https://example.com:444/piyo/fuga/",
+                          "https://example.com:80/piyo/fuga"}},
+
+      // Absolute URL with a non-default port
+      {.constructor_string = "https://example.com:444/piyo/fuga",
+       .expected_pattern = ExpectPatternInit(
+           /*protocol=*/"https", /*username=*/std::nullopt,
+           /*password=*/std::nullopt, /*hostname=*/"example.com",
+           /*port=*/"444", /*pathname=*/"/piyo/fuga",
+           /*search=*/std::nullopt,
+           /*hash=*/std::nullopt),
+       .match_urls = {"https://example.com:444/piyo/fuga"},
+       .non_match_urls = {"https://example.com/piyo/fuga/",
+                          "https://example.com:443/piyo/fuga",
+                          "https://example.com:80/piyo/fuga"}},
+
+      // Empty pathname
+      {.constructor_string = "https://example.com",
+       .expected_pattern = ExpectPatternInit(
+           /*protocol=*/"https", /*username=*/std::nullopt,
+           /*password=*/std::nullopt, /*hostname=*/"example.com",
+           /*port=*/"", /*pathname=*/std::nullopt,
+           /*search=*/std::nullopt,
+           /*hash=*/std::nullopt),
+       .match_urls = {"https://example.com", "https://example.com/piyo/"},
+       .non_match_urls = {"https://example.net/"}},
+
+      // pathname = `/`
+      {.constructor_string = "https://example.com/",
+       .expected_pattern = ExpectPatternInit(
+           /*protocol=*/"https", /*username=*/std::nullopt,
+           /*password=*/std::nullopt, /*hostname=*/"example.com",
+           /*port=*/"", /*pathname=*/"/",
+           /*search=*/std::nullopt,
+           /*hash=*/std::nullopt),
+       .match_urls = {"https://example.com"},
+       .non_match_urls = {"https://example.net/", "https://example.com/piyo/"}},
+
+      // Test cases for SimpleUrlPatternMatcher creation failure
+
+      // Relative path with a query string with a non-standard protocol
+      {.constructor_string = "non-standard:hoge?*",
+       .expected_error = "Protocol may not be omitted"},
+
+      // Escaped pathname
+      {.constructor_string = "\\/piyo\\/fuga",
+       .expected_error = "Protocol may not be omitted"},
+
+      // No slash in the non-standard base URL's pathname
+      {.constructor_string = "non-standard:hoge/piyo",
+       .expected_error = "Protocol may not be omitted"},
+
+      // Relative path
+      {.constructor_string = "/foo",
+       .expected_error = "Protocol may not be omitted"},
+
+      // Relative path ending with /
+      {.constructor_string = "/foo/",
+       .expected_error = "Protocol may not be omitted"},
+
+      // Relative path
+      {.constructor_string = "hoge",
+       .expected_error = "Protocol may not be omitted"},
+
+      // Relative path ending with /
+      {.constructor_string = "hoge/",
+       .expected_error = "Protocol may not be omitted"},
+
+      // Empty constructor string
+      {.constructor_string = "",
+       .expected_error = "Protocol may not be omitted"},
+
+      // Invalid constructor string (pathname)
+      {.constructor_string = "https://example.com/{",
+       .expected_error = "Failed to parse pattern for pathname"},
+
+      // Invalid constructor string (protocol)
+      {.constructor_string = "\t://example.com/",
+       .expected_error = "Failed to parse pattern for protocol"},
+
+      // Unsupported regexp group
+      {.constructor_string = "https://example.com/(\\d+)/",
+       .expected_error = "Regexp groups are not supported for pathname"}};
+
+  for (const auto& test : test_cases) {
+    SCOPED_TRACE(base::StrCat(
+        {"constructor_string: \"", test.constructor_string, "\""}));
+    if (!test.expected_error) {
+      ASSERT_OK_AND_ASSIGN(
+          auto matcher, CreateMatcherWithoutBaseUrl(test.constructor_string));
+      for (const auto& match_url : test.match_urls) {
+        EXPECT_TRUE(matcher->Match(GURL(match_url))) << match_url;
+      }
+      for (const auto& non_match_url : test.non_match_urls) {
+        EXPECT_FALSE(matcher->Match(GURL(non_match_url))) << non_match_url;
+      }
+    } else {
+      auto create_matcher_result =
+          CreateMatcherWithoutBaseUrl(test.constructor_string);
+      EXPECT_FALSE(create_matcher_result.has_value());
+      EXPECT_EQ(create_matcher_result.error(), *test.expected_error);
+    }
+  }
+}
 }  // namespace url_pattern
diff --git a/components/visited_url_ranking/internal/transformer/default_app_url_visit_aggregates_transformer.cc b/components/visited_url_ranking/internal/transformer/default_app_url_visit_aggregates_transformer.cc
index f143451b..8cf85db 100644
--- a/components/visited_url_ranking/internal/transformer/default_app_url_visit_aggregates_transformer.cc
+++ b/components/visited_url_ranking/internal/transformer/default_app_url_visit_aggregates_transformer.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "components/visited_url_ranking/public/url_visit.h"
+#include "third_party/abseil-cpp/absl/functional/overload.h"
 
 namespace {
 
@@ -15,7 +16,7 @@
 const GURL& GetVisitVariantUrl(
     const URLVisitAggregate::URLVisitVariant& visit_variant) {
   return std::visit(
-      visited_url_ranking::URLVisitVariantHelper{
+      absl::Overload{
           [&](const URLVisitAggregate::TabData& tab_data) -> const GURL& {
             return tab_data.last_active_tab.visit.url;
           },
diff --git a/components/visited_url_ranking/internal/visited_url_ranking_service_impl.cc b/components/visited_url_ranking/internal/visited_url_ranking_service_impl.cc
index 5cee937..7a9f9f6 100644
--- a/components/visited_url_ranking/internal/visited_url_ranking_service_impl.cc
+++ b/components/visited_url_ranking/internal/visited_url_ranking_service_impl.cc
@@ -48,6 +48,7 @@
 #include "components/visited_url_ranking/public/url_visit_schema.h"
 #include "components/visited_url_ranking/public/url_visit_util.h"
 #include "components/visited_url_ranking/public/visited_url_ranking_service.h"
+#include "third_party/abseil-cpp/absl/functional/overload.h"
 
 using segmentation_platform::AnnotatedNumericResult;
 using segmentation_platform::InputContext;
@@ -148,7 +149,7 @@
 
       URLVisitAggregate& aggregate = url_visit_map.at(url_data.first);
       std::visit(
-          URLVisitVariantHelper{
+          absl::Overload{
               [&aggregate](URLVisitAggregate::TabData& tab_data) {
                 aggregate.fetcher_data_map.emplace(
                     tab_data.last_active_tab.session_name.has_value()
diff --git a/components/visited_url_ranking/public/url_visit.cc b/components/visited_url_ranking/public/url_visit.cc
index 133ced2..a0eb45f 100644
--- a/components/visited_url_ranking/public/url_visit.cc
+++ b/components/visited_url_ranking/public/url_visit.cc
@@ -10,6 +10,8 @@
 #include <utility>
 #include <variant>
 
+#include "third_party/abseil-cpp/absl/functional/overload.h"
+
 namespace visited_url_ranking {
 
 URLVisit::URLVisit(const GURL& url_arg,
@@ -44,7 +46,7 @@
   std::set<std::u16string_view> titles = {};
   for (const auto& fetcher_entry : fetcher_data_map) {
     std::visit(
-        URLVisitVariantHelper{
+        absl::Overload{
             [&titles](const URLVisitAggregate::TabData& tab_data) {
               titles.insert(tab_data.last_active_tab.visit.title);
             },
@@ -59,7 +61,7 @@
 std::set<const GURL*> URLVisitAggregate::GetAssociatedURLs() const {
   std::set<const GURL*> urls = {};
   for (const auto& fetcher_entry : fetcher_data_map) {
-    std::visit(URLVisitVariantHelper{
+    std::visit(absl::Overload{
                    [&urls](const URLVisitAggregate::TabData& tab_data) {
                      urls.insert(&tab_data.last_active_tab.visit.url);
                    },
@@ -122,7 +124,7 @@
   URLVisitAggregate::URLTypeSet types;
   for (const auto& fetcher_entry : fetcher_data_map) {
     std::visit(
-        URLVisitVariantHelper{
+        absl::Overload{
             [&types](const URLVisitAggregate::TabData& tab_data) {
               if (tab_data.last_active_tab.session_name) {
                 types.Put(URLVisitAggregate::URLType::kActiveRemoteTab);
diff --git a/components/visited_url_ranking/public/url_visit.h b/components/visited_url_ranking/public/url_visit.h
index 82a17ec..e4a08c6 100644
--- a/components/visited_url_ranking/public/url_visit.h
+++ b/components/visited_url_ranking/public/url_visit.h
@@ -240,18 +240,6 @@
   std::vector<Decoration> decorations;
 };
 
-// Helper to visit each variant of URLVisitVariant.
-// Usage:
-//   std::visit(URLVisitVariantHelper{
-//         [](Variant1& variant1) {},
-//         [](Variant2& variant1) {},
-//         [](Variant3& variant1) {},
-//      variant_data);
-template <class... Ts>
-struct URLVisitVariantHelper : Ts... {
-  using Ts::operator()...;
-};
-
 }  // namespace visited_url_ranking
 
 #endif  // COMPONENTS_VISITED_URL_RANKING_PUBLIC_URL_VISIT_H_
diff --git a/components/viz/client/client_resource_provider.cc b/components/viz/client/client_resource_provider.cc
index 226a65d..f7cee0b 100644
--- a/components/viz/client/client_resource_provider.cc
+++ b/components/viz/client/client_resource_provider.cc
@@ -263,6 +263,8 @@
       };
 
   if (context_provider) {
+    CHECK(context_provider->SharedImageInterface());
+
     context_provider->SharedImageInterface()->VerifySyncTokens(
         imports, [](ImportedResource* imported) {
           return can_verify_resource(imported)
diff --git a/components/viz/common/resources/shared_image_format_utils_unittest.cc b/components/viz/common/resources/shared_image_format_utils_unittest.cc
index e46db8e..2025629 100644
--- a/components/viz/common/resources/shared_image_format_utils_unittest.cc
+++ b/components/viz/common/resources/shared_image_format_utils_unittest.cc
@@ -138,98 +138,5 @@
   EXPECT_EQ(kBGRA_8888_SkColorType, ToClosestSkColorType(format));
 }
 
-TEST_F(SharedImageFormatUtilsTest, SharedMemoryOffsetForSharedImageFormat) {
-  int widths[] = {1, 2, 3, 4, 8, 10, 29, 53, 64, 128};
-  for (int i = 0; i <= static_cast<int>(gfx::BufferFormat::LAST); ++i) {
-    auto buffer_format = static_cast<gfx::BufferFormat>(i);
-    auto format = GetSharedImageFormat(buffer_format);
-    if (format.is_single_plane()) {
-      continue;
-    }
-    for (int plane = 0; plane < format.NumberOfPlanes(); ++plane) {
-      for (int width : widths) {
-        gfx::Size size(width, 128);
-        size_t buffer_offset =
-            SharedMemoryOffsetForSharedImageFormat(format, plane, size);
-        size_t expected_buffer_offset =
-            gfx::BufferOffsetForBufferFormat(size, buffer_format, plane);
-        EXPECT_EQ(buffer_offset, expected_buffer_offset);
-      }
-    }
-  }
-}
-
-TEST_F(SharedImageFormatUtilsTest, SharedMemoryRowSizeForSharedImageFormat) {
-  int widths[] = {1, 2, 3, 4, 8, 10, 29, 53, 64, 128};
-  for (int i = 0; i <= static_cast<int>(gfx::BufferFormat::LAST); ++i) {
-    auto buffer_format = static_cast<gfx::BufferFormat>(i);
-    auto format = GetSharedImageFormat(buffer_format);
-    if (format == SinglePlaneFormat::kETC1) {
-      continue;
-    }
-    for (int plane = 0; plane < format.NumberOfPlanes(); ++plane) {
-      for (int width : widths) {
-        std::optional<size_t> row_bytes =
-            SharedMemoryRowSizeForSharedImageFormat(format, plane, width);
-        EXPECT_TRUE(row_bytes);
-
-        size_t expected_row_bytes = 0;
-        bool valid = gfx::RowSizeForBufferFormatChecked(
-            width, buffer_format, plane, &expected_row_bytes);
-        EXPECT_TRUE(valid);
-        EXPECT_EQ(*row_bytes, expected_row_bytes);
-      }
-    }
-  }
-}
-
-TEST_F(SharedImageFormatUtilsTest, SharedMemoryPlaneSizeForSharedImageFormat) {
-  int widths[] = {1, 2, 3, 4, 8, 10, 29, 53, 64, 128};
-  for (int i = 0; i <= static_cast<int>(gfx::BufferFormat::LAST); ++i) {
-    auto buffer_format = static_cast<gfx::BufferFormat>(i);
-    auto format = GetSharedImageFormat(buffer_format);
-    if (format == SinglePlaneFormat::kETC1) {
-      continue;
-    }
-    for (int plane = 0; plane < format.NumberOfPlanes(); ++plane) {
-      for (int width : widths) {
-        auto size = gfx::Size(width, width);
-        std::optional<size_t> plane_bytes =
-            SharedMemoryPlaneSizeForSharedImageFormat(format, plane, size);
-        EXPECT_TRUE(plane_bytes);
-
-        size_t expected_plane_bytes = 0;
-        bool valid = gfx::PlaneSizeForBufferFormatChecked(
-            size, buffer_format, plane, &expected_plane_bytes);
-        EXPECT_TRUE(valid);
-        EXPECT_EQ(*plane_bytes, expected_plane_bytes);
-      }
-    }
-  }
-}
-
-TEST_F(SharedImageFormatUtilsTest, SharedMemorySizeForSharedImageFormat) {
-  int widths[] = {1, 2, 3, 4, 8, 10, 29, 53, 64, 128};
-  for (int i = 0; i <= static_cast<int>(gfx::BufferFormat::LAST); ++i) {
-    auto buffer_format = static_cast<gfx::BufferFormat>(i);
-    auto format = GetSharedImageFormat(buffer_format);
-    if (format == SinglePlaneFormat::kETC1) {
-      continue;
-    }
-    for (int width : widths) {
-      auto size = gfx::Size(width, width);
-      std::optional<size_t> buffer_bytes =
-          SharedMemorySizeForSharedImageFormat(format, size);
-      EXPECT_TRUE(buffer_bytes);
-
-      size_t expected_buffer_bytes = 0;
-      bool valid = gfx::BufferSizeForBufferFormatChecked(
-          size, buffer_format, &expected_buffer_bytes);
-      EXPECT_TRUE(valid);
-      EXPECT_EQ(*buffer_bytes, expected_buffer_bytes);
-    }
-  }
-}
-
 }  // namespace
 }  // namespace viz
diff --git a/components/viz/host/gpu_client.cc b/components/viz/host/gpu_client.cc
index e4f681d..20321f6d 100644
--- a/components/viz/host/gpu_client.cc
+++ b/components/viz/host/gpu_client.cc
@@ -20,10 +20,12 @@
 GpuClient::GpuClient(std::unique_ptr<GpuClientDelegate> delegate,
                      int client_id,
                      uint64_t client_tracing_id,
+                     bool enable_extra_handles_validation,
                      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
     : delegate_(std::move(delegate)),
       client_id_(client_id),
       client_tracing_id_(client_tracing_id),
+      enable_extra_handles_validation_(enable_extra_handles_validation),
       task_runner_(std::move(task_runner)) {
   DCHECK(delegate_);
   gpu_receivers_.set_disconnect_handler(
@@ -187,7 +189,8 @@
   gpu_channel_requested_ = true;
   const bool is_gpu_host = false;
   gpu_host->EstablishGpuChannel(
-      client_id_, client_tracing_id_, is_gpu_host, false,
+      client_id_, client_tracing_id_, is_gpu_host,
+      enable_extra_handles_validation_, false,
       base::BindOnce(&GpuClient::OnEstablishGpuChannel,
                      weak_factory_.GetWeakPtr()));
 }
diff --git a/components/viz/host/gpu_client.h b/components/viz/host/gpu_client.h
index 708a927..371d006a 100644
--- a/components/viz/host/gpu_client.h
+++ b/components/viz/host/gpu_client.h
@@ -32,6 +32,7 @@
   GpuClient(std::unique_ptr<GpuClientDelegate> delegate,
             int client_id,
             uint64_t client_tracing_id,
+            bool enable_extra_handles_validation,
             scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
   GpuClient(const GpuClient&) = delete;
@@ -89,6 +90,7 @@
   std::unique_ptr<GpuClientDelegate> delegate_;
   const int client_id_;
   const uint64_t client_tracing_id_;
+  const bool enable_extra_handles_validation_;
 
   mojo::ReceiverSet<mojom::Gpu> gpu_receivers_;
   bool gpu_channel_requested_ = false;
diff --git a/components/viz/host/gpu_host_impl.cc b/components/viz/host/gpu_host_impl.cc
index ff9c61f..18ee95b 100644
--- a/components/viz/host/gpu_host_impl.cc
+++ b/components/viz/host/gpu_host_impl.cc
@@ -264,11 +264,13 @@
 void GpuHostImpl::EstablishGpuChannel(int client_id,
                                       uint64_t client_tracing_id,
                                       bool is_gpu_host,
+                                      bool enable_extra_handles_validation,
                                       bool sync,
                                       EstablishChannelCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   TRACE_EVENT2("gpu", "GpuHostImpl::EstablishGpuChannel", "client_id",
                client_id, "is_gpu_host", is_gpu_host);
+  DCHECK(!(is_gpu_host && enable_extra_handles_validation));
 
   shutdown_timeout_.Stop();
 
@@ -291,7 +293,8 @@
     {
       mojo::SyncCallRestrictions::ScopedAllowSyncCall scoped_allow;
       gpu_service_remote_->EstablishGpuChannel(
-          client_id, client_tracing_id, is_gpu_host, &channel_handle, &gpu_info,
+          client_id, client_tracing_id, is_gpu_host,
+          enable_extra_handles_validation, &channel_handle, &gpu_info,
           &gpu_feature_info, &shared_image_capabilities);
     }
     OnChannelEstablished(client_id, true, std::move(channel_handle), gpu_info,
@@ -299,6 +302,7 @@
   } else {
     gpu_service_remote_->EstablishGpuChannel(
         client_id, client_tracing_id, is_gpu_host,
+        enable_extra_handles_validation,
         base::BindOnce(&GpuHostImpl::OnChannelEstablished,
                        weak_ptr_factory_.GetWeakPtr(), client_id, false));
   }
diff --git a/components/viz/host/gpu_host_impl.h b/components/viz/host/gpu_host_impl.h
index df4d6fa..127ce2f 100644
--- a/components/viz/host/gpu_host_impl.h
+++ b/components/viz/host/gpu_host_impl.h
@@ -192,6 +192,7 @@
   void EstablishGpuChannel(int client_id,
                            uint64_t client_tracing_id,
                            bool is_gpu_host,
+                           bool enable_extra_handles_validation,
                            bool sync,
                            EstablishChannelCallback callback);
   void SetChannelClientPid(int client_id, base::ProcessId client_pid);
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index 8db64fd..b97af79 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -152,7 +152,8 @@
 }
 
 void DirectRenderer::DecideRenderPassAllocationsForFrame(
-    const AggregatedRenderPassList& render_passes_in_draw_order) {
+    const AggregatedRenderPassList& render_passes_in_draw_order,
+    bool skip_root_render_pass_allocation) {
   DCHECK(render_pass_bypass_quads_.empty());
 
   auto& root_render_pass = render_passes_in_draw_order.back();
@@ -162,26 +163,9 @@
   for (const auto& pass : render_passes_in_draw_order) {
     const bool is_root = pass == root_render_pass;
 
-#if BUILDFLAG(IS_WIN)
-    // For delegated compositing the root pass is preserved, but not rendered.
-    // If a previous frame fell out of delegated compositing we want to make
-    // sure that we deallocate its backing when switching back to delegated
-    // compositing.
-    if (is_root && output_surface_->capabilities().renderer_allocates_images &&
-        !current_frame()->output_surface_plane) {
-      // We expect to be in delegated compositing mode, which means the root
-      // damage rect has been cleared.
-      CHECK(current_frame()->root_damage_rect.IsEmpty());
+    if (is_root && skip_root_render_pass_allocation) {
       continue;
     }
-#else
-    // TODO(crbug.com/40224327): Consider deallocating the primary plane in this
-    // case.
-    // Non-Windows platforms use BufferQueue, which are not owned by the render
-    // pass backing. ChromeOS must hold on to the root surface buffers to ensure
-    // overlay-ability and macOS wants to just discard the underlying surfaces
-    // for performance.
-#endif
 
     const RenderPassRequirements requirements =
         CalculateRenderPassRequirements(pass.get());
@@ -266,16 +250,17 @@
     }
   }
 
-  bool frame_has_alpha =
-      current_frame()->root_render_pass->has_transparent_background;
   gfx::ColorSpace frame_color_space =
       RenderPassColorSpace(current_frame()->root_render_pass);
   SharedImageFormat frame_si_format = GetSharedImageFormat(
       current_frame()->display_color_spaces.GetOutputBufferFormat(
           current_frame()->root_render_pass->content_color_usage,
-          frame_has_alpha));
+          current_frame()->root_render_pass->has_transparent_background));
   gfx::Size surface_resource_size =
       CalculateSizeForOutputSurface(device_viewport_size);
+#if BUILDFLAG(IS_WIN)
+  bool has_primary_plane = false;
+#endif
   if (overlay_processor_) {
     // Display transform and viewport size are needed for overlay validator on
     // Android SurfaceControl, and viewport size is need on Windows. These need
@@ -286,12 +271,23 @@
 
     // Before ProcessForOverlay calls into the hardware to ask about whether the
     // overlay setup can be handled, we need to set up the primary plane.
+    std::optional<OverlayCandidate> primary_plane;
     if (output_surface_->capabilities().renderer_allocates_images) {
-      current_frame()->output_surface_plane =
-          overlay_processor_->ProcessOutputSurfaceAsOverlay(
-              device_viewport_size, surface_resource_size, frame_si_format,
-              frame_color_space, frame_has_alpha, 1.0f /*opacity*/,
-              GetPrimaryPlaneOverlayTestingMailbox());
+      primary_plane = overlay_processor_->ProcessOutputSurfaceAsOverlay(
+          device_viewport_size, surface_resource_size, frame_si_format,
+          frame_color_space,
+          current_frame()->root_render_pass->has_transparent_background,
+          1.0f /*opacity*/, GetPrimaryPlaneOverlayTestingMailbox());
+
+      if (current_frame()->display_color_spaces.SupportsHDR() &&
+          current_frame()->root_render_pass->content_color_usage ==
+              gfx::ContentColorUsage::kHDR) {
+        primary_plane->hdr_metadata.extended_range.emplace();
+        // TODO(crbug.com/40263227): Track the actual brightness of the
+        // content. For now, assume that all HDR content is 1,000 nits.
+        primary_plane->hdr_metadata.extended_range->desired_headroom =
+            gfx::HdrMetadataExtendedRange::kDefaultHdrHeadroom;
+      }
     }
 
     // Attempt to replace some or all of the quads of the root render pass with
@@ -301,7 +297,7 @@
         resource_provider_, render_passes_in_draw_order,
         output_surface_->color_matrix(), render_pass_filters_,
         render_pass_backdrop_filters_, std::move(surface_damage_rect_list),
-        current_frame()->output_surface_plane, &current_frame()->overlay_list,
+        primary_plane, &current_frame()->overlay_list,
         &current_frame()->root_damage_rect,
         &current_frame()->root_content_bounds);
     auto overlay_processing_time = overlay_processing_timer.Elapsed();
@@ -313,17 +309,36 @@
         "Compositing.DirectRenderer.OverlayProcessingUs",
         overlay_processing_time, kMinTime, kMaxTime, kTimeBuckets);
 
-    // If we promote any quad to an underlay then the main plane must support
-    // alpha.
-    // TODO(ccameron): We should update |frame_color_space|, and
-    // |frame_si_format| based on the change in |frame_has_alpha|.
-    if (current_frame()->output_surface_plane) {
-      frame_has_alpha |= !current_frame()->output_surface_plane->is_opaque;
-      root_render_pass->has_transparent_background = frame_has_alpha;
-    }
+    overlay_processor_->AdjustOutputSurfaceOverlay(primary_plane);
 
-    overlay_processor_->AdjustOutputSurfaceOverlay(
-        current_frame()->output_surface_plane);
+    if (primary_plane) {
+#if BUILDFLAG(IS_OZONE)
+      // Ozone DRM needs the primary plane as the first overlay when overlay
+      // testing.
+      const auto insert_positon = current_frame()->overlay_list.begin();
+      current_frame()->overlay_list.insert(insert_positon,
+                                           std::move(primary_plane).value());
+#elif BUILDFLAG(IS_MAC)
+      // Mac doesn't use the plane_z_order field and it needs to have primary
+      // plane last in the list of overlays.
+      current_frame()->overlay_list.push_back(std::move(primary_plane).value());
+#elif BUILDFLAG(IS_ANDROID)
+      // Android respects plane_z_order and order in the list shouldn't matter,
+      // but it surfaces the bug when the planes are not hidden properly. As we
+      // use only underlays, we should keep primary plane first so it would hide
+      // planes that are not supposed to be visible.
+      const auto insert_positon = current_frame()->overlay_list.begin();
+      current_frame()->overlay_list.insert(insert_positon,
+                                           std::move(primary_plane).value());
+#else
+      // Other platforms respect plane_z_order so the list order doesn't matter.
+      current_frame()->overlay_list.push_back(std::move(primary_plane).value());
+#endif
+
+#if BUILDFLAG(IS_WIN)
+      has_primary_plane = true;
+#endif
+    }
   }
 
   // Only reshape when we know we are going to draw. Otherwise, the reshape
@@ -337,8 +352,9 @@
   reshape_params.device_scale_factor = device_scale_factor;
   reshape_params.color_space = frame_color_space;
   reshape_params.format = frame_si_format;
-  reshape_params.alpha_type = frame_has_alpha ? RenderPassAlphaType::kPremul
-                                              : RenderPassAlphaType::kOpaque;
+  reshape_params.alpha_type = root_render_pass->has_transparent_background
+                                  ? RenderPassAlphaType::kPremul
+                                  : RenderPassAlphaType::kOpaque;
   if (next_frame_needs_full_frame_redraw_ ||
       reshape_params != reshape_params_ ||
       display_transform != reshape_display_transform_) {
@@ -356,7 +372,7 @@
     // If compositing is delegated, then there will be no output_surface_plane,
     // and we should not trigger a redraw of the root render pass.
     // Pixel tests will not be displayed as overlay planes, so they need redraw.
-    if (current_frame()->output_surface_plane ||
+    if (has_primary_plane ||
         !output_surface_->capabilities().renderer_allocates_images) {
       needs_full_frame_redraw = true;
     }
@@ -366,12 +382,30 @@
 #endif
   }
 
-  // DecideRenderPassAllocationsForFrame needs
-  // current_frame()->display_color_spaces to decide the color space
-  // of each render pass. Overlay processing is also allowed to modify the
-  // render pass backing requirements due to e.g. a underlay promotion. On
-  // Windows, the root render pass' size is based on the |reshape_params_|.
-  DecideRenderPassAllocationsForFrame(*render_passes_in_draw_order);
+#if BUILDFLAG(IS_WIN)
+  // For delegated compositing the root pass is preserved, but not rendered.
+  // If a previous frame fell out of delegated compositing we want to make
+  // sure that we deallocate its backing when switching back to delegated
+  // compositing.
+  const bool skip_root_render_pass_allocation =
+      output_surface_->capabilities().renderer_allocates_images &&
+      !has_primary_plane;
+  if (skip_root_render_pass_allocation) {
+    // We expect to be in delegated compositing mode, which means the root
+    // damage rect has been cleared.
+    CHECK(current_frame()->root_damage_rect.IsEmpty());
+  }
+#else
+  // TODO(crbug.com/40224327): Consider deallocating the primary plane in this
+  // case.
+  // Non-Windows platforms use BufferQueue, which are not owned by the render
+  // pass backing. ChromeOS must hold on to the root surface buffers to ensure
+  // overlay-ability and macOS wants to just discard the underlying surfaces
+  // for performance.
+  const bool skip_root_render_pass_allocation = false;
+#endif
+  DecideRenderPassAllocationsForFrame(*render_passes_in_draw_order,
+                                      skip_root_render_pass_allocation);
 
   // Draw all non-root render passes except for the root render pass.
   total_pixels_rendered_this_frame_ = 0;
@@ -869,13 +903,9 @@
     requirements.generate_mipmap = false;
     requirements.color_space = reshape_color_space();
     requirements.format = reshape_si_format();
-    if (is_root) {
-      requirements.alpha_type = reshape_alpha_type();
-    } else {
-      requirements.alpha_type = render_pass->has_transparent_background
-                                    ? RenderPassAlphaType::kPremul
-                                    : RenderPassAlphaType::kOpaque;
-    }
+    requirements.alpha_type = render_pass->has_transparent_background
+                                  ? RenderPassAlphaType::kPremul
+                                  : RenderPassAlphaType::kOpaque;
   } else {
     requirements.generate_mipmap = render_pass->generate_mipmap;
     requirements.color_space = RenderPassColorSpace(render_pass);
diff --git a/components/viz/service/display/direct_renderer.h b/components/viz/service/display/direct_renderer.h
index baa4764..dfe82e8 100644
--- a/components/viz/service/display/direct_renderer.h
+++ b/components/viz/service/display/direct_renderer.h
@@ -83,7 +83,8 @@
   void SetVisible(bool visible);
   void ReallocatedFrameBuffers();
   void DecideRenderPassAllocationsForFrame(
-      const AggregatedRenderPassList& render_passes_in_draw_order);
+      const AggregatedRenderPassList& render_passes_in_draw_order,
+      bool skip_root_render_pass_allocation);
   void DrawFrame(AggregatedRenderPassList* render_passes_in_draw_order,
                  float device_scale_factor,
                  const gfx::Size& device_viewport_size,
@@ -152,10 +153,6 @@
     gfx::AxisTransform2d target_to_device_transform;
 
     OverlayProcessorInterface::CandidateList overlay_list;
-    // When we have a buffer queue, the output surface could be treated as an
-    // overlay plane, and the struct to store that information is in
-    // |output_surface_plane|.
-    std::optional<OverlayCandidate> output_surface_plane;
   };
 
   void SetCurrentFrameForTesting(const DrawingFrame& frame);
@@ -409,10 +406,6 @@
     DCHECK(reshape_params_);
     return reshape_params_->color_space;
   }
-  RenderPassAlphaType reshape_alpha_type() const {
-    DCHECK(reshape_params_);
-    return reshape_params_->alpha_type;
-  }
 
   // Sets a DelegatedInkPointRendererSkiaForTest to be used for testing only, in
   // order to save delegated ink metadata values that would otherwise be reset.
diff --git a/components/viz/service/display/overlay_ca_unittest.cc b/components/viz/service/display/overlay_ca_unittest.cc
index 53d2d34..bde95ff 100644
--- a/components/viz/service/display/overlay_ca_unittest.cc
+++ b/components/viz/service/display/overlay_ca_unittest.cc
@@ -33,6 +33,7 @@
 #include "components/viz/service/display/output_surface_frame.h"
 #include "components/viz/service/display/overlay_processor_mac.h"
 #include "components/viz/test/fake_skia_output_surface.h"
+#include "components/viz/test/overlay_candidate_matchers.h"
 #include "components/viz/test/test_context_provider.h"
 #include "components/viz/test/test_gles2_interface.h"
 #include "gpu/command_buffer/client/client_shared_image.h"
@@ -190,6 +191,14 @@
     output_surface_ = nullptr;
   }
 
+  std::optional<OverlayCandidate>& GetDefaultPrimaryPlane(
+      const gfx::Size& size) {
+    primary_plane_ = OverlayProcessorInterface::ProcessOutputSurfaceAsOverlay(
+        size, size, SinglePlaneFormat::kRGBA_8888,
+        gfx::ColorSpace::CreateSRGB(), false, 1.0, gpu::Mailbox());
+    return primary_plane_;
+  }
+
   std::unique_ptr<SkiaOutputSurface> output_surface_;
   cc::FakeOutputSurfaceClient output_surface_client_;
   std::unique_ptr<DisplayResourceProviderSkia> resource_provider_;
@@ -198,9 +207,11 @@
   scoped_refptr<TestContextProvider> child_provider_;
   std::unique_ptr<ClientResourceProvider> child_resource_provider_;
   std::unique_ptr<CATestOverlayProcessor> overlay_processor_;
-  std::optional<OverlayCandidate> null_primary_plane_;
   gfx::Rect damage_rect_ = kOverlayDamageRect;
   std::vector<gfx::Rect> content_bounds_;
+
+ private:
+  std::optional<OverlayCandidate> primary_plane_;
 };
 
 TEST_F(CALayerOverlayTest, AllowNonAxisAlignedTransform) {
@@ -221,10 +232,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &ca_layer_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &ca_layer_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(gfx::Rect(), damage_rect_);
-  EXPECT_EQ(1U, ca_layer_list.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list));
   gfx::Rect overlay_damage = overlay_processor_->GetAndResetOverlayDamage();
   EXPECT_EQ(kRenderPassOutputRect, overlay_damage);
 }
@@ -247,9 +259,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &ca_layer_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(1U, ca_layer_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &ca_layer_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list));
   gfx::Rect overlay_damage = overlay_processor_->GetAndResetOverlayDamage();
   EXPECT_EQ(kRenderPassOutputRect, overlay_damage);
   gfx::Transform expected_transform;
@@ -276,10 +289,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &ca_layer_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &ca_layer_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(gfx::Rect(), damage_rect_);
-  EXPECT_EQ(1U, ca_layer_list.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list));
 }
 
 TEST_F(CALayerOverlayTest, NontrivialClip) {
@@ -299,10 +313,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &ca_layer_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &ca_layer_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(gfx::Rect(), damage_rect_);
-  EXPECT_EQ(1U, ca_layer_list.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list));
   EXPECT_EQ(gfx::Rect(64, 64, 128, 128),
             ca_layer_list.back().clip_rect.value());
 }
@@ -324,10 +339,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &ca_layer_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &ca_layer_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(gfx::Rect(), damage_rect_);
-  EXPECT_EQ(0U, ca_layer_list.size());
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list));
 }
 
 TEST_F(CALayerOverlayTest, SkipNonVisible) {
@@ -347,10 +363,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &ca_layer_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &ca_layer_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(gfx::Rect(), damage_rect_);
-  EXPECT_EQ(0U, ca_layer_list.size());
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list));
 }
 
 TEST_F(CALayerOverlayTest, TextureDrawQuadVideoOverlay) {
@@ -383,10 +400,11 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &ca_layer_list, &damage_rect_, &content_bounds_);
     EXPECT_EQ(gfx::Rect(), damage_rect_);
-    EXPECT_EQ(1U, ca_layer_list.size());
+    EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list));
   }
 }
 
@@ -409,12 +427,13 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &ca_layer_list, &damage_rect_, &content_bounds_);
 
     // There should be no error.
     gfx::CALayerResult error_code = overlay_processor_->GetCALayerErrorCode();
-    EXPECT_EQ(1U, ca_layer_list.size());
+    EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list));
     // kCALayerSuccess = 0,
     EXPECT_EQ(0, error_code);
   }
@@ -437,11 +456,12 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &ca_layer_list, &damage_rect_, &content_bounds_);
 
     // Overlay should fail when there is a copy request.
-    EXPECT_EQ(0U, ca_layer_list.size());
+    EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list));
 
     // kCALayerFailedCopyRequests = 31,
     gfx::CALayerResult error_code = overlay_processor_->GetCALayerErrorCode();
@@ -463,8 +483,9 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list_, GetIdentityColorMatrix(),
         render_pass_filters_, render_pass_backdrop_filters_,
-        std::move(surface_damage_rect_list_), null_primary_plane_,
-        &ca_layer_list_, &damage_rect_, &content_bounds_);
+        std::move(surface_damage_rect_list_),
+        GetDefaultPrimaryPlane(pass_->output_rect.size()), &ca_layer_list_,
+        &damage_rect_, &content_bounds_);
   }
   AggregatedRenderPassList pass_list_;
   raw_ptr<AggregatedRenderPass> pass_;
@@ -485,7 +506,7 @@
                 false, 1.0f);
   ProcessForOverlays();
 
-  EXPECT_EQ(1U, ca_layer_list_.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list_));
 }
 
 TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadAllValidFilters) {
@@ -507,7 +528,7 @@
                 false, 1.0f);
   ProcessForOverlays();
 
-  EXPECT_EQ(1U, ca_layer_list_.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list_));
 }
 
 TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadOpacityFilterScale) {
@@ -518,7 +539,7 @@
                 gfx::Size(), gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(),
                 false, 1.0f);
   ProcessForOverlays();
-  EXPECT_EQ(1U, ca_layer_list_.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list_));
 }
 
 TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadBlurFilterScale) {
@@ -529,7 +550,7 @@
                 gfx::Size(), gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(),
                 false, 1.0f);
   ProcessForOverlays();
-  EXPECT_EQ(1U, ca_layer_list_.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list_));
 }
 
 TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadDropShadowFilterScale) {
@@ -541,7 +562,7 @@
                 gfx::Size(), gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(),
                 false, 1.0f);
   ProcessForOverlays();
-  EXPECT_EQ(1U, ca_layer_list_.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list_));
 }
 
 TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadBackgroundFilter) {
@@ -552,7 +573,7 @@
                 gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(),
                 false, 1.0f);
   ProcessForOverlays();
-  EXPECT_EQ(0U, ca_layer_list_.size());
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list_));
 }
 
 TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadMask) {
@@ -561,7 +582,7 @@
                 gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(),
                 false, 1.0f);
   ProcessForOverlays();
-  EXPECT_EQ(1U, ca_layer_list_.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list_));
 }
 
 TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadUnsupportedFilter) {
@@ -572,7 +593,7 @@
                 gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(),
                 false, 1.0f);
   ProcessForOverlays();
-  EXPECT_EQ(0U, ca_layer_list_.size());
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list_));
 }
 
 TEST_F(CALayerOverlayRPDQTest, TooManyRenderPassDrawQuads) {
@@ -591,7 +612,7 @@
   }
 
   ProcessForOverlays();
-  EXPECT_EQ(0U, ca_layer_list_.size());
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list_));
 }
 
 }  // namespace
diff --git a/components/viz/service/display/overlay_dc_unittest.cc b/components/viz/service/display/overlay_dc_unittest.cc
index dfb1e53..d1419cc 100644
--- a/components/viz/service/display/overlay_dc_unittest.cc
+++ b/components/viz/service/display/overlay_dc_unittest.cc
@@ -2682,7 +2682,7 @@
 
     EXPECT_TRUE(overlay_processor_->IsOverlaySupported());
 
-    output_surface_plane_ = OverlayCandidate();
+    output_surface_plane_ = GetDefaultPrimaryPlane(gfx::Size(256, 256));
   }
 
   void TearDown() override {
@@ -2690,8 +2690,11 @@
     OverlayProcessorTestBase::TearDown();
   }
 
-  std::optional<OverlayCandidate>& GetOutputSurfacePlane() {
-    return output_surface_plane_;
+  std::optional<OverlayCandidate> GetDefaultPrimaryPlane(
+      const gfx::Size& primary_plane_size) {
+    return overlay_processor_->ProcessOutputSurfaceAsOverlay(
+        primary_plane_size, primary_plane_size, SinglePlaneFormat::kBGRA_8888,
+        gfx::ColorSpace::CreateSRGB(), false, 1.0, gpu::Mailbox());
   }
 
   std::optional<OverlayCandidate> output_surface_plane_;
@@ -2842,7 +2845,8 @@
                                                      &damage_rect_);
     }
 
-    output_surface_plane_ = OverlayCandidate();
+    output_surface_plane_ =
+        GetDefaultPrimaryPlane(render_passes->back()->output_rect.size());
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), render_passes, SkM44(), render_pass_filters,
         render_pass_backdrop_filters,
@@ -3287,8 +3291,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), GetOutputSurfacePlane(),
-        &candidates, &damage_rect_, &content_bounds_);
+        std::move(surface_damage_rect_list), output_surface_plane_, &candidates,
+        &damage_rect_, &content_bounds_);
 
     overlay_processor_->AdjustOutputSurfaceOverlay(output_surface_plane_);
     const bool delegation_succeeded = !output_surface_plane_.has_value();
diff --git a/components/viz/service/display/overlay_processor_delegated.cc b/components/viz/service/display/overlay_processor_delegated.cc
index 86730d48..6e00da4 100644
--- a/components/viz/service/display/overlay_processor_delegated.cc
+++ b/components/viz/service/display/overlay_processor_delegated.cc
@@ -216,6 +216,10 @@
     previous_frame_overlay_rect_ = gfx::Rect();
     // This is only relevant when delegating.
     unassigned_damage_ = gfx::RectF();
+
+    CHECK(primary_plane);
+    render_passes->back()->has_transparent_background |=
+        !primary_plane->is_opaque;
   }
 
   DebugLogAfterDelegation(delegated_status_, *candidates, *damage_rect);
diff --git a/components/viz/service/display/overlay_processor_mac.cc b/components/viz/service/display/overlay_processor_mac.cc
index ac9c9bcf..0d71a4b5 100644
--- a/components/viz/service/display/overlay_processor_mac.cc
+++ b/components/viz/service/display/overlay_processor_mac.cc
@@ -85,6 +85,9 @@
         resource_provider, render_pass, gfx::RectF(render_pass->output_rect),
         &render_pass->quad_list, render_pass_filters,
         render_pass_backdrop_filters, candidates);
+
+    CHECK(primary_plane);
+    render_pass->has_transparent_background |= !primary_plane->is_opaque;
   }
 }
 
diff --git a/components/viz/service/display/overlay_processor_using_strategy.cc b/components/viz/service/display/overlay_processor_using_strategy.cc
index cde8f7b..17026c9 100644
--- a/components/viz/service/display/overlay_processor_using_strategy.cc
+++ b/components/viz/service/display/overlay_processor_using_strategy.cc
@@ -407,6 +407,10 @@
         primary_plane, candidates, content_bounds, damage_rect);
   }
 
+  if (primary_plane) {
+    render_pass->has_transparent_background |= !primary_plane->is_opaque;
+  }
+
   DCHECK(candidates->empty() || success);
   UMA_HISTOGRAM_COUNTS_100(kNumOverlaysPromotedHistogramName,
                            candidates->size());
diff --git a/components/viz/service/display/overlay_processor_win.cc b/components/viz/service/display/overlay_processor_win.cc
index 8734acca..3939118 100644
--- a/components/viz/service/display/overlay_processor_win.cc
+++ b/components/viz/service/display/overlay_processor_win.cc
@@ -222,6 +222,8 @@
     CHECK(primary_plane);
     primary_plane->is_opaque =
         !render_passes->back()->has_transparent_background;
+    primary_plane->layer_id = gfx::OverlayLayerId::MakeVizInternalRenderPass(
+        render_passes->back()->id);
   }
 
   if (is_page_fullscreen_mode_ &&
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc
index 450a05e5..f5b4887 100644
--- a/components/viz/service/display/overlay_unittest.cc
+++ b/components/viz/service/display/overlay_unittest.cc
@@ -62,6 +62,7 @@
 #include "components/viz/service/display/overlay_strategy_underlay.h"
 #include "components/viz/service/display/test_resource_factory.h"
 #include "components/viz/test/fake_skia_output_surface.h"
+#include "components/viz/test/overlay_candidate_matchers.h"
 #include "components/viz/test/test_context_provider.h"
 #include "components/viz/test/test_gles2_interface.h"
 #include "gpu/config/gpu_finch_features.h"
@@ -907,11 +908,19 @@
     return resource_factory_->resource_provider();
   }
 
+  std::optional<OverlayCandidate>& GetDefaultPrimaryPlane(
+      const gfx::Size& size) {
+    primary_plane_ = OverlayProcessorInterface::ProcessOutputSurfaceAsOverlay(
+        size, size, SinglePlaneFormat::kRGBA_8888,
+        gfx::ColorSpace::CreateSRGB(), false, 1.0, gpu::Mailbox());
+    return primary_plane_;
+  }
+
   std::unique_ptr<TestResourceFactory> resource_factory_;
   std::unique_ptr<OverlayProcessorType> overlay_processor_;
-  std::optional<OverlayCandidate> null_primary_plane_;
   gfx::Rect damage_rect_;
   std::vector<gfx::Rect> content_bounds_;
+  std::optional<OverlayCandidate> primary_plane_;
 };
 
 template <typename OverlayProcessorType>
@@ -989,20 +998,21 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   if (base::FeatureList::IsEnabled(
           features::kUseDrmBlackFullscreenOptimization)) {
     // Check that all the quads are gone.
     EXPECT_EQ(0U, main_pass->quad_list.size());
     // Check that we have only one overlay.
-    EXPECT_EQ(1U, candidate_list.size());
+    EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
     // Check that the candidate has replaced the primary plane.
     EXPECT_EQ(candidate_list[0].plane_z_order, 0);
   } else {
     // No fullscreen promotion is possible.
-    EXPECT_EQ(0U, candidate_list.size());
+    EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   }
 }
 
@@ -1047,20 +1057,21 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   if (base::FeatureList::IsEnabled(
           features::kUseDrmBlackFullscreenOptimization)) {
     // Check that all the quads are gone.
     EXPECT_EQ(0U, main_pass->quad_list.size());
     // Check that we have only one overlay.
-    EXPECT_EQ(1U, candidate_list.size());
+    EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
     // Check that the candidate has replaced the primary plane.
     EXPECT_EQ(candidate_list[0].plane_z_order, 0);
   } else {
     // No fullscreen promotion is possible.
-    EXPECT_EQ(0U, candidate_list.size());
+    EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   }
 }
 
@@ -1086,15 +1097,16 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
-  ASSERT_EQ(1U, candidate_list.size());
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 
   // Check that all the quads are gone.
   EXPECT_EQ(0U, main_pass->quad_list.size());
   // Check that we have only one overlay.
-  EXPECT_EQ(1U, candidate_list.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   // Check that the right resource id got extracted.
   EXPECT_EQ(original_resource_id, candidate_list.front().resource_id);
   gfx::Rect overlay_damage_rect =
@@ -1127,9 +1139,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 
   // Check that the 2 quads are not gone.
   EXPECT_EQ(2U, main_pass->quad_list.size());
@@ -1157,9 +1170,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetNonIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 
   // Check that the 2 quads are not gone.
   EXPECT_EQ(2U, main_pass->quad_list.size());
@@ -1182,13 +1196,14 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   // Check that all the quads are gone.
   EXPECT_EQ(1U, main_pass->quad_list.size());
   // Check that we have only one overlay.
-  EXPECT_EQ(0U, candidate_list.size());
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(FullscreenOverlayTest, OnTopFail) {
@@ -1213,9 +1228,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 
   // Check that the 2 quads are not gone.
   EXPECT_EQ(2U, main_pass->quad_list.size());
@@ -1240,15 +1256,16 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
   if (base::FeatureList::IsEnabled(
           features::kUseDrmBlackFullscreenOptimization)) {
-    ASSERT_EQ(1U, candidate_list.size());
+    ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
     // Check that the quad is gone.
     EXPECT_EQ(0U, main_pass->quad_list.size());
   } else {
-    ASSERT_EQ(0U, candidate_list.size());
+    ASSERT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
     // Check that the quad is not gone.
     EXPECT_EQ(1U, main_pass->quad_list.size());
   }
@@ -1279,9 +1296,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 
   // Check that the fullscreen quad is gone.
   for (const DrawQuad* quad : main_pass->quad_list) {
@@ -1311,9 +1329,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 
   // Check that the quad is gone.
   EXPECT_EQ(2U, main_pass->quad_list.size());
@@ -1324,7 +1343,7 @@
   }
 
   // Check that the right resource id got extracted.
-  EXPECT_EQ(original_resource_id, candidate_list.back().resource_id);
+  EXPECT_EQ(original_resource_id, candidate_list[0].resource_id);
 }
 
 TEST_F(MultiSingleOnTopOverlayTest,
@@ -1356,14 +1375,15 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   // Since the rounded-display mask quad is an overlay candidate, it will not
   // occlude the other potential single-on-top candidate. Both the
   // rounded-display mask quad and other single-on-top candidate quad will get
   // promoted.
-  ASSERT_EQ(2U, candidate_list.size());
+  ASSERT_EQ(2U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 
   // Check that the two quads are gone.
   EXPECT_EQ(1U, main_pass->quad_list.size());
@@ -1405,11 +1425,12 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   // The first two candidates are promoted as they are not occluded.
-  ASSERT_EQ(2U, candidate_list.size());
+  ASSERT_EQ(2U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 
   // Candidate with mask candidate should be composited since it is not drawn
   // on top.
@@ -1446,8 +1467,9 @@
   EXPECT_DCHECK_DEATH(overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_));
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_));
 }
 
 TEST_F(MultiSingleOnTopOverlayTest,
@@ -1493,11 +1515,12 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   // All the quads will get promoted.
-  ASSERT_EQ(3U, candidate_list.size());
+  ASSERT_EQ(3U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_EQ(0U, main_pass->quad_list.size());
 }
 
@@ -1537,11 +1560,12 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   // Only the candidate without rounded-display masks will be promoted.
-  ASSERT_EQ(1U, candidate_list.size());
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   // Confirm that candidates with rounded-display masks are not promoted.
   EXPECT_FALSE(candidate_list.back().has_rounded_display_masks);
   EXPECT_EQ(2U, main_pass->quad_list.size());
@@ -1598,10 +1622,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
-  ASSERT_EQ(3U, candidate_list.size());
+  ASSERT_EQ(3U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 
   // Check that the top quad is gone.
   EXPECT_EQ(1U, main_pass->quad_list.size());
@@ -1661,10 +1686,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &render_pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      surface_damage_rect_list, null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      surface_damage_rect_list,
+      GetDefaultPrimaryPlane(render_pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
-  ASSERT_EQ(1U, candidate_list.size());
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_EQ(low_latency_quad_resource_id, candidate_list[0].resource_id);
 }
 
@@ -1701,14 +1727,15 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 
   // Check that one quad is gone.
   EXPECT_EQ(2U, main_pass->quad_list.size());
   // Check that we have only one overlay.
-  EXPECT_EQ(1U, candidate_list.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   // Check that the right resource id (bigger quad) got extracted.
   EXPECT_EQ(resource_big, candidate_list.front().resource_id);
 }
@@ -1762,13 +1789,14 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, color_mat, render_pass_filters,
       render_pass_backdrop_filters, std::move(surface_damage_rect_list),
-      null_primary_plane_, &candidate_list, &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 
   // Check that one quad is gone.
   EXPECT_EQ(2U, main_pass->quad_list.size());
   // Check that we have only one overlay.
-  EXPECT_EQ(1U, candidate_list.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   // Check that the right resource id (the first one) got extracted.
   EXPECT_EQ(resource_a, candidate_list.front().resource_id);
 }
@@ -1875,9 +1903,10 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
-    ASSERT_EQ(1U, candidate_list.size());
+    ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 
     if (prev_resource != candidate_list.front().resource_id) {
       if (prev_resource != kInvalidResourceId) {
@@ -1940,7 +1969,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
     EXPECT_EQ(damage_rect_, kExpectedDamage[i]);
@@ -1991,7 +2021,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
     EXPECT_EQ(damage_rect_, kExpectedDamage[i]);
   }
@@ -2055,7 +2086,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
     EXPECT_EQ(damage_rect_, kExpectedDamage[i]);
   }
@@ -2093,8 +2125,9 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_TRUE(damage_rect_.IsEmpty());
 }
 
@@ -2135,8 +2168,9 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_TRUE(damage_rect_.IsEmpty());
 }
 
@@ -2159,9 +2193,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   // There should be nothing new here.
   CompareRenderPassLists(pass_list, original_pass_list);
 }
@@ -2189,9 +2224,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   // There should be nothing new here.
   CompareRenderPassLists(pass_list, original_pass_list);
 }
@@ -2217,9 +2253,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, AcceptBlending) {
@@ -2240,9 +2277,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_FALSE(damage_rect_.IsEmpty());
   gfx::Rect overlay_damage_rect =
       overlay_processor_->GetAndResetOverlayDamage();
@@ -2266,9 +2304,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, AcceptBlackBackgroundColor) {
@@ -2288,9 +2327,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 // MaskFilters are only supported by underlay strategy.
@@ -2317,9 +2357,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 // MaskFilters are only supported by underlay strategy.
@@ -2348,9 +2389,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, RejectBlackBackgroundColorWithBlending) {
@@ -2371,9 +2413,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, RejectBlendMode) {
@@ -2393,9 +2436,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, RejectOpacity) {
@@ -2415,9 +2459,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, RejectNearestNeighbor) {
@@ -2438,9 +2483,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, RejectNonAxisAlignedTransform) {
@@ -2461,9 +2507,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, AllowClipped) {
@@ -2483,9 +2530,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(UnderlayTest, ReplacementQuad) {
@@ -2504,8 +2552,9 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, pass_list.size());
   ASSERT_EQ(1U, pass_list.front()->quad_list.size());
   EXPECT_EQ(SkColors::kTransparent, static_cast<SolidColorDrawQuad*>(
@@ -2535,11 +2584,12 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL,
-            std::get<gfx::OverlayTransform>(candidate_list.back().transform));
+            std::get<gfx::OverlayTransform>(candidate_list[0].transform));
 }
 
 TEST_F(UnderlayTest, AllowHorizontalFlip) {
@@ -2561,11 +2611,12 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL,
-            std::get<gfx::OverlayTransform>(candidate_list.back().transform));
+            std::get<gfx::OverlayTransform>(candidate_list[0].transform));
 }
 
 TEST_F(SingleOverlayOnTopTest, AllowPositiveScaleTransform) {
@@ -2585,9 +2636,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, AcceptMirrorYTransform) {
@@ -2608,9 +2660,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(UnderlayTest, Allow90DegreeRotation) {
@@ -2631,11 +2684,12 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_90,
-            std::get<gfx::OverlayTransform>(candidate_list.back().transform));
+            std::get<gfx::OverlayTransform>(candidate_list[0].transform));
 }
 
 TEST_F(UnderlayTest, Allow180DegreeRotation) {
@@ -2656,11 +2710,12 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_180,
-            std::get<gfx::OverlayTransform>(candidate_list.back().transform));
+            std::get<gfx::OverlayTransform>(candidate_list[0].transform));
 }
 
 TEST_F(UnderlayTest, Allow270DegreeRotation) {
@@ -2681,11 +2736,12 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_270,
-            std::get<gfx::OverlayTransform>(candidate_list.back().transform));
+            std::get<gfx::OverlayTransform>(candidate_list[0].transform));
 }
 
 TEST_F(UnderlayTest, AllowsOpaqueCandidates) {
@@ -2704,9 +2760,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(UnderlayTest, DisallowsQuadsWithRoundedDisplayMasks) {
@@ -2728,10 +2785,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
-  ASSERT_EQ(0U, candidate_list.size());
+  ASSERT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(UnderlayTest, DisallowsTransparentCandidates) {
@@ -2749,9 +2807,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(UnderlayTest, DisallowFilteredQuadOnTop) {
@@ -2784,9 +2843,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(UnderlayTest, AllowFilteredQuadOnTopForProtectedVideo) {
@@ -2821,9 +2881,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(TransparentUnderlayTest, AllowsOpaqueCandidates) {
@@ -2842,9 +2903,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(TransparentUnderlayTest, AllowsTransparentCandidates) {
@@ -2862,9 +2924,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, AllowNotTopIfNotOccluded) {
@@ -2887,9 +2950,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, AllowTransparentOnTop) {
@@ -2914,9 +2978,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, AllowTransparentColorOnTop) {
@@ -2940,9 +3005,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, RejectOpaqueColorOnTop) {
@@ -2965,9 +3031,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(SingleOverlayOnTopTest, RejectTransparentColorOnTopWithoutBlending) {
@@ -2988,9 +3055,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 // Test makes sure promotion hint (|overlay_priority_hint| in |TextureDrawQuad|)
@@ -3052,24 +3120,25 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
     if (i == kTestRegularAtFrame) {
-      EXPECT_EQ(1U, candidate_list.size());
+      EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
       if (!candidate_list.empty()) {
         // Check that it was the partial damaged one that got promoted.
         EXPECT_EQ(kOverlayTopLeftRect,
-                  gfx::ToRoundedRect(candidate_list.back().display_rect));
+                  gfx::ToRoundedRect(candidate_list[0].display_rect));
       }
     } else if (i == kTestRequiredAtFrame) {
-      EXPECT_EQ(1U, candidate_list.size());
+      EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
       if (!candidate_list.empty()) {
         // Check that it was the partial damaged one that got promoted.
         EXPECT_EQ(kOverlayTopLeftRect,
-                  gfx::ToRoundedRect(candidate_list.back().display_rect));
+                  gfx::ToRoundedRect(candidate_list[0].display_rect));
         // Also check that the required flag is set.
-        EXPECT_TRUE(candidate_list.back().requires_overlay);
+        EXPECT_TRUE(candidate_list[0].requires_overlay);
       }
     }
   }
@@ -3125,14 +3194,15 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
     frame_counter++;
     if (i <= kFramesSkippedBeforeNotPromoting) {
-      EXPECT_EQ(1U, candidate_list.size());
+      EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
       if (candidate_list.size()) {
         // Check that the right resource id got extracted.
-        EXPECT_EQ(resource_id, candidate_list.back().resource_id);
+        EXPECT_EQ(resource_id, candidate_list[0].resource_id);
       }
       // Check that the quad is gone.
       EXPECT_EQ(1U, main_pass->quad_list.size());
@@ -3216,17 +3286,18 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
     EXPECT_EQ(3u, main_pass->quad_list.size());
 
     if (i == kDamageFrameTestStart - 1 || i == kSlowFrameTestStart - 1) {
       // Test to make sure an overlay was promoted.
-      EXPECT_EQ(1U, candidate_list.size());
+      EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
     } else if (i == kDamageFrameTestEnd - 1 || i == kSlowFrameTestEnd - 1) {
       // Test to make sure no overlay was promoted
-      EXPECT_EQ(0u, candidate_list.size());
+      EXPECT_EQ(0u, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
     }
   }
 }
@@ -3364,7 +3435,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
     EXPECT_EQ(2u, candidates_tracker_map.size());
@@ -3411,13 +3483,13 @@
         i >= kContentNoChangeEnd) {
       // When no occlusion quad is not paused, we expect both quads to be
       // promoted with the no occlusion quad being the top candidate.
-      EXPECT_EQ(2u, candidate_list.size());
+      EXPECT_EQ(2u, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
       EXPECT_EQ(no_occlusion_quad_resource_id, candidate_list[0].resource_id);
       EXPECT_EQ(with_occlusion_quad_resource_id, candidate_list[1].resource_id);
     } else {
       // When the no occlusion quad is paused, we expect only the occluded quad
       // is promoted.
-      EXPECT_EQ(1u, candidate_list.size());
+      EXPECT_EQ(1u, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
       EXPECT_EQ(with_occlusion_quad_resource_id, candidate_list[0].resource_id);
     }
   }
@@ -3443,9 +3515,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_EQ(-1, candidate_list[0].plane_z_order);
   EXPECT_EQ(2U, main_pass->quad_list.size());
   // The overlay quad should have changed to a SOLID_COLOR quad.
@@ -3476,9 +3549,10 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
-  ASSERT_EQ(1U, candidate_list.size());
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_EQ(-1, candidate_list[0].plane_z_order);
   // The overlay quad should have changed to a SOLID_COLOR quad.
   EXPECT_EQ(main_pass->quad_list.front()->material,
@@ -3535,10 +3609,11 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
-    ASSERT_EQ(candidate_list.size(), 1U);
+    ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 1u);
 
     if (i < kOverlayFrameStart) {
       // A pure overlay does not produce damage on promotion and all associated
@@ -3607,10 +3682,11 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
-    ASSERT_EQ(candidate_list.size(), 1U);
+    ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 1u);
 
     if (i < kTransparentFrameStart) {
       // A pure overlay does not produce damage on promotion and all associated
@@ -3659,10 +3735,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
-  ASSERT_EQ(candidate_list.size(), 1U);
+  ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 1u);
   EXPECT_LT(candidate_list.front().plane_z_order, 0);
 
   ASSERT_EQ(1U, pass_list.size());
@@ -3697,8 +3774,9 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(kOverlayRect, damage_rect_);
 }
@@ -3741,7 +3819,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
     if (i == 0) {
@@ -3794,7 +3873,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
     // This is a union as the demoted underlay display rect is added as damage
@@ -3845,7 +3925,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
     if (i == 0) {
       EXPECT_FALSE(damage_rect_.IsEmpty());
@@ -3905,7 +3986,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
     if (i == 0) {
       EXPECT_FALSE(damage_rect_.IsEmpty());
@@ -3955,7 +4037,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
     // The damage rect should not be excluded if the underlay has been promoted
@@ -4009,7 +4092,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
   }
 
@@ -4045,7 +4129,7 @@
       std::move(surface_damage_rect_list), output_surface_plane,
       &candidate_list, &damage_rect_, &content_bounds_);
 
-  EXPECT_EQ(1U, candidate_list.size());
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   ASSERT_FALSE(output_surface_plane->is_opaque);
 }
 
@@ -4075,7 +4159,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
   }
 
@@ -4339,11 +4424,13 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_background_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect, &content_bounds_);
 
     EXPECT_EQ(expected_damages[i], damage_rect);
-    ASSERT_EQ(expected_candidate_size[i], candidate_list.size());
+    ASSERT_EQ(expected_candidate_size[i],
+              test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   }
 }
 
@@ -4392,7 +4479,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
     if (i == 0 || i == 1) {
@@ -4426,7 +4514,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
+      std::move(surface_damage_rect_list), primary_plane_, &candidate_list,
       &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, pass_list.size());
   ASSERT_EQ(1U, pass_list.front()->quad_list.size());
@@ -4455,7 +4543,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
+      std::move(surface_damage_rect_list), primary_plane_, &candidate_list,
       &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, content_bounds_.size());
 }
@@ -4475,7 +4563,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
+      std::move(surface_damage_rect_list), primary_plane_, &candidate_list,
       &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
@@ -4506,7 +4594,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
+      std::move(surface_damage_rect_list), primary_plane_, &candidate_list,
       &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
@@ -4530,7 +4618,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
+      std::move(surface_damage_rect_list), primary_plane_, &candidate_list,
       &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
@@ -4556,7 +4644,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
+      std::move(surface_damage_rect_list), primary_plane_, &candidate_list,
       &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
@@ -4588,7 +4676,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
+      std::move(surface_damage_rect_list), primary_plane_, &candidate_list,
       &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
@@ -4621,7 +4709,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
+      std::move(surface_damage_rect_list), primary_plane_, &candidate_list,
       &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
@@ -4643,7 +4731,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
+      std::move(surface_damage_rect_list), primary_plane_, &candidate_list,
       &damage_rect_, &content_bounds_);
 
   ASSERT_TRUE(candidate_list.empty());
@@ -4671,7 +4759,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
+      std::move(surface_damage_rect_list), primary_plane_, &candidate_list,
       &damage_rect_, &content_bounds_);
 
   ASSERT_EQ(1U, content_bounds_.size());
@@ -4716,8 +4804,9 @@
       std::move(surface_damage_rect_list), output_surface_plane,
       &candidate_list, &damage_rect_, &content_bounds_);
 
-  ASSERT_EQ(false, output_surface_plane->is_opaque);
   EXPECT_EQ(0U, content_bounds_.size());
+  ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
+  ASSERT_EQ(false, output_surface_plane->is_opaque);
 }
 #endif  // BUILDFLAG(ALWAYS_ENABLE_BLENDING_FOR_PRIMARY)
 
@@ -5034,9 +5123,10 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
-    ASSERT_EQ(1U, candidate_list.size());
+    ASSERT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   }
 }
 
@@ -5099,7 +5189,8 @@
         surface_damage_rect_list, output_surface_plane, &candidate_list,
         &damage_rect_, &content_bounds_);
 
-    EXPECT_EQ(expected_overlays[i], candidate_list.size() > 0);
+    EXPECT_EQ(expected_overlays[i],
+              test::NumOverlaysExcludingPrimaryPlane(candidate_list) > 0);
   }
 }
 
@@ -5250,7 +5341,7 @@
       std::move(surface_damage_rect_list),
       overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list,
       &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0u, candidate_list.size());
+  EXPECT_EQ(0u, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
 }
 
 TEST_F(DelegatedTest, BlockDelegationWithNonRootCopies) {
@@ -5300,7 +5391,8 @@
         render_pass_filters, render_pass_backdrop_filters,
         surface_damage_rect_list, overlay_processor_->GetDefaultPrimaryPlane(),
         &candidate_list, &damage_rect_, &content_bounds_);
-    EXPECT_EQ(expected_overlays[i], candidate_list.size());
+    EXPECT_EQ(expected_overlays[i],
+              test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   }
 }
 
@@ -5332,7 +5424,7 @@
       &damage_rect_, &content_bounds_);
 
   const auto uv_rect = gfx::RectF(0, 0.5f, 0.5f, 0.5f);
-  EXPECT_EQ(1U, candidate_list.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_RECTF_NEAR(gfx::RectF(kTestClip), candidate_list[0].display_rect,
                     0.01f);
   EXPECT_RECTF_NEAR(uv_rect, candidate_list[0].uv_rect, 0.01f);
@@ -5366,7 +5458,7 @@
       &damage_rect_, &content_bounds_);
 
   const auto uv_rect = gfx::RectF(0, 0.5f, 0.5f, 0.5f);
-  EXPECT_EQ(1U, candidate_list.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_RECTF_NEAR(gfx::RectF(kTestClip), candidate_list[0].display_rect,
                     0.01f);
   EXPECT_RECTF_NEAR(uv_rect, candidate_list[0].uv_rect, 0.01f);
@@ -5398,7 +5490,7 @@
       overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list,
       &damage_rect_, &content_bounds_);
 
-  EXPECT_EQ(1U, candidate_list.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   auto expected_rect = kTestClip;
   expected_rect.Intersect(kSmallCandidateRect);
   gfx::RectF uv_rect = cc::MathUtil::ScaleRectProportional(
@@ -5440,7 +5532,7 @@
       overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list,
       &damage_rect_, &content_bounds_);
 
-  EXPECT_EQ(1U, candidate_list.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   // Render pass clip rect gets applied because clip delegation is not enabled.
   EXPECT_RECTF_NEAR(gfx::RectF(5, 15, 65, 59), candidate_list[0].display_rect,
                     0.01f);
@@ -5476,7 +5568,7 @@
       &damage_rect_, &content_bounds_);
 
   const auto uv_rect = gfx::RectF(0, 0.0f, 0.5f, 0.5f);
-  EXPECT_EQ(1U, candidate_list.size());
+  EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   // We clip all rects to the primary display to ensure delegated and composited
   // paths have identical results.
   EXPECT_RECTF_NEAR(gfx::RectF(gfx::Rect(kDisplaySize)),
@@ -5521,7 +5613,8 @@
       &damage_rect_, &content_bounds_);
 
   // Damage is not assigned to specific candidates.
-  EXPECT_EQ(main_pass->quad_list.size(), candidate_list.size());
+  EXPECT_EQ(main_pass->quad_list.size(),
+            test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_TRUE(damage_rect_.IsEmpty());
   EXPECT_TRUE(candidate_list[0].damage_rect.IsEmpty());
   EXPECT_TRUE(candidate_list[1].damage_rect.IsEmpty());
@@ -5558,7 +5651,8 @@
       overlay_processor_->GetDefaultPrimaryPlane(), &candidate_list,
       &damage_rect_, &content_bounds_);
 
-  EXPECT_EQ(main_pass->quad_list.size(), candidate_list.size());
+  EXPECT_EQ(main_pass->quad_list.size(),
+            test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_TRUE(damage_rect_.IsEmpty());
 }
 
@@ -5614,10 +5708,11 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
-    EXPECT_EQ(0U, candidate_list.size());
+    EXPECT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
     EXPECT_EQ(damage_rect_, kOverlayRect);
   }
 };
@@ -5694,7 +5789,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
     if (i == 0) {
@@ -5759,7 +5855,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
     if (i == 0) {
@@ -5832,7 +5929,8 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
     if (i < kPromotionFrame) {
@@ -5913,10 +6011,11 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
-    ASSERT_EQ(candidate_list.size(), 1u);
+    ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 1u);
 
     if (i == 0) {
       // Damage for both candidates
@@ -6005,11 +6104,12 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
 
     if (i < kTopRightGone) {
-      ASSERT_EQ(candidate_list.size(), 2u);
+      ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 2u);
       // Transparent overlay
       EXPECT_GT(candidate_list[0].plane_z_order, 0);
       EXPECT_FALSE(candidate_list[0].is_opaque);
@@ -6017,7 +6117,7 @@
       EXPECT_LT(candidate_list[1].plane_z_order, 0);
       EXPECT_TRUE(candidate_list[1].is_opaque);
     } else {
-      ASSERT_EQ(candidate_list.size(), 1u);
+      ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 1u);
       // Transparent overlay
       EXPECT_GT(candidate_list[0].plane_z_order, 0);
       EXPECT_FALSE(candidate_list[0].is_opaque);
@@ -6088,10 +6188,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
-  ASSERT_EQ(candidate_list.size(), 1u);
+  ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 1u);
   // Fullscreen overlay
   EXPECT_EQ(candidate_list[0].plane_z_order, 0);
   EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kFullRect);
@@ -6149,10 +6250,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
-  ASSERT_EQ(candidate_list.size(), 1u);
+  ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 1u);
   // Only the required overlay is promoted.
   EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kBottomLeft);
   EXPECT_TRUE(candidate_list[0].requires_overlay);
@@ -6203,10 +6305,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
-  ASSERT_EQ(candidate_list.size(), 4u);
+  ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 4u);
   // Expect the first four candidates promoted to on top overlays.
   EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kCandRects[0]);
   EXPECT_EQ(candidate_list[0].plane_z_order, 1);
@@ -6281,14 +6384,15 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   // Since the mask candidate in top right will fail to promote, we will also
   // composite the SingleOnTop candidate occlude by this failing candidate. The
   // underlay candidate will promoted normally since it is not effected by the
   // failing mask candidate (even though it is occluded).
-  ASSERT_EQ(candidate_list.size(), 3u);
+  ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 3u);
 
   EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kNormalLeft);
   EXPECT_EQ(candidate_list[0].plane_z_order, 1);
@@ -6364,13 +6468,14 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   // Since both the overlay (SingleOnTop) candidates without masks failed to
   // promote, we end up composting both candidates with masks as well. Only the
   // underlay candidate is promoted.
-  ASSERT_EQ(candidate_list.size(), 1u);
+  ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 1u);
 
   EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kOverlayRect);
   EXPECT_EQ(candidate_list[0].plane_z_order, -1);
@@ -6410,14 +6515,15 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   // None of the candidates will be promoted.
   // Since AllowCandidateWithMasksSortedMultiOverlayProcessor will only allow
   // candidates with rounded-display masks after sorting(i.e only candidates to
   // pass sorting hubristic), we can skip promoting these candidates.
-  ASSERT_EQ(0U, candidate_list.size());
+  ASSERT_EQ(0U, test::NumOverlaysExcludingPrimaryPlane(candidate_list));
   EXPECT_EQ(3U, main_pass->quad_list.size());
 }
 
@@ -6490,10 +6596,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
-  ASSERT_EQ(candidate_list.size(), 4u);
+  ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 4u);
 
   // We expect the two rounded masks to get promoted regardless of their surface
   // area. Candidates with rounded masks are followed by candidate with largest
@@ -6554,9 +6661,10 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters,
-        std::move(surface_damage_rect_list), null_primary_plane_,
+        std::move(surface_damage_rect_list),
+        GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
         &candidate_list, &damage_rect_, &content_bounds_);
-    ASSERT_EQ(candidate_list.size(), 1u);
+    ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 1u);
     if (i == 0) {
       EXPECT_EQ(candidate_list[0].plane_z_order, -1);
     }
@@ -6613,10 +6721,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
-  ASSERT_EQ(candidate_list.size(), 4u);
+  ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 4u);
   // Expect all four are promoted to overlays, and their plane_z_order is based
   // on draw order.
   EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kBiggest);
@@ -6678,10 +6787,11 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters,
-      std::move(surface_damage_rect_list), null_primary_plane_, &candidate_list,
-      &damage_rect_, &content_bounds_);
+      std::move(surface_damage_rect_list),
+      GetDefaultPrimaryPlane(pass_list.back()->output_rect.size()),
+      &candidate_list, &damage_rect_, &content_bounds_);
 
-  ASSERT_EQ(candidate_list.size(), 4u);
+  ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 4u);
   // Expect all four are promoted to underlay, and their plane_z_order is based
   // on draw order.
   EXPECT_EQ(gfx::ToRoundedRect(candidate_list[0].display_rect), kBiggest);
@@ -6750,12 +6860,12 @@
 
   if (promoted) {
     // Both candidates are promoted.
-    ASSERT_EQ(candidate_list.size(), 2u);
+    ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 2u);
     // Blending enabled on primary plane.
     EXPECT_FALSE(output_surface_plane->is_opaque);
   } else {
     // No candidates are promoted.
-    EXPECT_TRUE(candidate_list.empty());
+    ASSERT_EQ(test::NumOverlaysExcludingPrimaryPlane(candidate_list), 0u);
     // Blending not enabled on primary plane.
     EXPECT_TRUE(output_surface_plane->is_opaque);
   }
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index ec14b10..a16356a 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -1045,74 +1045,6 @@
   MaybeScheduleBackgroundImage(current_frame()->overlay_list);
 #endif  // BUILDFLAG(IS_OZONE)
 
-  // TODO(weiliangc): Remove this once OverlayProcessor schedules overlays.
-  if (current_frame()->output_surface_plane) {
-    CHECK(output_surface_->capabilities().renderer_allocates_images);
-
-    auto& surface_candidate = current_frame()->output_surface_plane.value();
-    auto root_pass_backing =
-        render_pass_backings_.find(current_frame()->root_render_pass->id);
-    // The root pass backing should always exist.
-    DCHECK(root_pass_backing != render_pass_backings_.end());
-    surface_candidate.mailbox = root_pass_backing->second.mailbox;
-    if (current_frame()->display_color_spaces.SupportsHDR() &&
-        current_frame()->root_render_pass->content_color_usage ==
-            gfx::ContentColorUsage::kHDR) {
-      surface_candidate.hdr_metadata.extended_range.emplace();
-      // TODO(crbug.com/40263227): Track the actual brightness of the
-      // content. For now, assume that all HDR content is 1,000 nits.
-      surface_candidate.hdr_metadata.extended_range->desired_headroom =
-          gfx::HdrMetadataExtendedRange::kDefaultHdrHeadroom;
-    }
-    surface_candidate.damage_rect =
-        use_partial_swap_
-            ? gfx::RectF(swap_buffer_rect_)
-            : gfx::RectF(surface_candidate.resource_size_in_pixels);
-#if BUILDFLAG(IS_WIN)
-    surface_candidate.layer_id = gfx::OverlayLayerId::MakeVizInternalRenderPass(
-        current_frame()->root_render_pass->id);
-#endif
-#if BUILDFLAG(IS_OZONE)
-    // Ozone DRM needs the primary plane as the first overlay when overlay
-    // testing.
-    const auto insert_positon = current_frame()->overlay_list.begin();
-    current_frame()->overlay_list.insert(
-        insert_positon,
-        std::move(current_frame()->output_surface_plane).value());
-#elif BUILDFLAG(IS_MAC)
-    // Mac doesn't use the plane_z_order field and it needs to have primary
-    // plane last in the list of overlays.
-    current_frame()->overlay_list.push_back(
-        std::move(current_frame()->output_surface_plane).value());
-#elif BUILDFLAG(IS_ANDROID)
-    // Android respects plane_z_order and order in the list shouldn't matter,
-    // but it surfaces the bug when the planes are not hidden properly. As we
-    // use only underlays, we should keep primary plane first so it would hide
-    // planes that are not supposed to be visible.
-    const auto insert_positon = current_frame()->overlay_list.begin();
-    current_frame()->overlay_list.insert(
-        insert_positon,
-        std::move(current_frame()->output_surface_plane).value());
-#else
-    // Other platforms respect plane_z_order so the list order doesn't matter.
-    current_frame()->overlay_list.push_back(
-        std::move(current_frame()->output_surface_plane).value());
-#endif
-
-  } else {
-    if (buffer_queue_) {
-      // If there's no primary plane on these platforms it mean's we're
-      // delegating to the system compositor, and don't need the buffers
-      // anymore. On Mac the primary plane buffers are marked as purgeable so
-      // the OS can decide if they should be destroyed or not.
-#if BUILDFLAG(IS_WIN)
-      buffer_queue_->DestroyBuffers();
-#elif BUILDFLAG(IS_APPLE)
-      buffer_queue_->SetBuffersPurgeable();
-#endif
-    }
-  }
-
   ScheduleOverlays();
   debug_tint_modulate_count_++;
 }
@@ -2890,8 +2822,20 @@
   DCHECK(output_surface_->capabilities().supports_surfaceless);
 #endif
 
+  bool has_primary_plane_overlay = false;
+
   for (auto& overlay : current_frame()->overlay_list) {
     if (overlay.is_root_render_pass) {
+      CHECK(output_surface_->capabilities().renderer_allocates_images);
+
+      auto root_pass_backing =
+          render_pass_backings_.find(current_frame()->root_render_pass->id);
+      // The root pass backing should always exist.
+      DCHECK(root_pass_backing != render_pass_backings_.end());
+      overlay.mailbox = root_pass_backing->second.mailbox;
+      overlay.damage_rect = gfx::RectF(swap_buffer_rect_);
+
+      has_primary_plane_overlay = true;
       continue;
     }
 
@@ -3002,6 +2946,20 @@
   DCHECK(!current_gpu_commands_completed_fence_->was_set());
   DCHECK(!current_release_fence_->was_set());
 
+  if (!has_primary_plane_overlay) {
+    if (buffer_queue_) {
+      // If there's no primary plane on these platforms it mean's we're
+      // delegating to the system compositor, and don't need the buffers
+      // anymore. On Mac the primary plane buffers are marked as purgeable so
+      // the OS can decide if they should be destroyed or not.
+#if BUILDFLAG(IS_WIN)
+      buffer_queue_->DestroyBuffers();
+#elif BUILDFLAG(IS_APPLE)
+      buffer_queue_->SetBuffersPurgeable();
+#endif
+    }
+  }
+
   skia_output_surface_->ScheduleOverlays(
       std::move(current_frame()->overlay_list), std::move(sync_tokens));
 }
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index ba5abd2..6b5d7d3f 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -37,6 +37,7 @@
 #include "components/viz/common/quads/surface_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/common/resources/resource_id.h"
+#include "components/viz/common/resources/shared_image_format.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/common/surfaces/subtree_capture_id.h"
 #include "components/viz/service/display/aggregated_frame.h"
@@ -6244,20 +6245,20 @@
                                         0.5)}}};
 
   gfx::DisplayColorSpaces display_color_spaces(gfx::ColorSpace::CreateSRGB());
-  display_color_spaces.SetOutputColorSpaceAndBufferFormat(
+  display_color_spaces.SetOutputColorSpaceAndFormat(
       gfx::ContentColorUsage::kWideColorGamut, false /* needs_alpha */,
       gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT2020,
                       gfx::ColorSpace::TransferID::SRGB),
-      gfx::BufferFormat::RGBA_8888);
-  display_color_spaces.SetOutputColorSpaceAndBufferFormat(
+      SinglePlaneFormat::kRGBA_8888);
+  display_color_spaces.SetOutputColorSpaceAndFormat(
       gfx::ContentColorUsage::kWideColorGamut, true /* needs_alpha */,
-      gfx::ColorSpace::CreateSRGBLinear(), gfx::BufferFormat::RGBA_8888);
-  display_color_spaces.SetOutputColorSpaceAndBufferFormat(
+      gfx::ColorSpace::CreateSRGBLinear(), SinglePlaneFormat::kRGBA_8888);
+  display_color_spaces.SetOutputColorSpaceAndFormat(
       gfx::ContentColorUsage::kHDR, false /* needs_alpha */,
-      gfx::ColorSpace::CreateHDR10(), gfx::BufferFormat::BGRA_1010102);
-  display_color_spaces.SetOutputColorSpaceAndBufferFormat(
+      gfx::ColorSpace::CreateHDR10(), SinglePlaneFormat::kBGRA_1010102);
+  display_color_spaces.SetOutputColorSpaceAndFormat(
       gfx::ContentColorUsage::kHDR, true /* needs_alpha */,
-      gfx::ColorSpace::CreateSRGBLinear(), gfx::BufferFormat::RGBA_F16);
+      gfx::ColorSpace::CreateSRGBLinear(), SinglePlaneFormat::kRGBA_F16);
 
   std::vector<Pass> passes = {
       Pass(quads[0], CompositorRenderPassId{2}, kSurfaceSize),
@@ -6346,14 +6347,14 @@
   // content can be drawn into a BT2020 buffer as 10-10-10-2, but transparent
   // content needs to bump up to 16-bit, and therefore (until we find a way
   // around this) linear color space.
-  display_color_spaces.SetOutputColorSpaceAndBufferFormat(
+  display_color_spaces.SetOutputColorSpaceAndFormat(
       gfx::ContentColorUsage::kHDR, false /* needs_alpha */,
       gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT2020,
                       gfx::ColorSpace::TransferID::SRGB),
-      gfx::BufferFormat::BGRA_1010102);
-  display_color_spaces.SetOutputColorSpaceAndBufferFormat(
+      SinglePlaneFormat::kBGRA_1010102);
+  display_color_spaces.SetOutputColorSpaceAndFormat(
       gfx::ContentColorUsage::kHDR, true /* needs_alpha */,
-      gfx::ColorSpace::CreateSRGBLinear(), gfx::BufferFormat::RGBA_F16);
+      gfx::ColorSpace::CreateSRGBLinear(), SinglePlaneFormat::kRGBA_F16);
 
   // Opaque content renders to the appropriate space directly.
   passes[1].has_transparent_background = false;
diff --git a/components/viz/service/display_embedder/compositor_gpu_thread.cc b/components/viz/service/display_embedder/compositor_gpu_thread.cc
index 381a4f4..29d821d 100644
--- a/components/viz/service/display_embedder/compositor_gpu_thread.cc
+++ b/components/viz/service/display_embedder/compositor_gpu_thread.cc
@@ -15,6 +15,7 @@
 #include "components/viz/common/features.h"
 #include "components/viz/common/gpu/vulkan_context_provider.h"
 #include "components/viz/common/switches.h"
+#include "gpu/command_buffer/service/feature_info.h"
 #include "gpu/config/gpu_finch_features.h"
 #include "gpu/ipc/common/gpu_client_ids.h"
 #include "gpu/ipc/service/gpu_channel_manager.h"
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index 8ec4db0..8d2d1c5 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -866,6 +866,7 @@
 void GpuServiceImpl::EstablishGpuChannel(int32_t client_id,
                                          uint64_t client_tracing_id,
                                          bool is_gpu_host,
+                                         bool enable_extra_handles_validation,
                                          EstablishGpuChannelCallback callback) {
   // This should always be called on the IO thread first.
   if (io_runner_->BelongsToCurrentThread()) {
@@ -890,14 +891,15 @@
     main_runner_->PostTask(
         FROM_HERE, base::BindOnce(&GpuServiceImpl::EstablishGpuChannel,
                                   weak_ptr_, client_id, client_tracing_id,
-                                  is_gpu_host, std::move(wrap_callback)));
+                                  is_gpu_host, enable_extra_handles_validation,
+                                  std::move(wrap_callback)));
     return;
   }
 
   auto channel_token = base::UnguessableToken::Create();
   gpu::GpuChannel* gpu_channel = gpu_channel_manager_->EstablishChannel(
       channel_token, client_id, client_tracing_id, is_gpu_host,
-      gpu_extra_info_);
+      enable_extra_handles_validation, gpu_extra_info_);
 
   if (!gpu_channel) {
     // This returns a null handle, which is treated by the client as a failure
diff --git a/components/viz/service/gl/gpu_service_impl.h b/components/viz/service/gl/gpu_service_impl.h
index 3b1d8779..9db4c00 100644
--- a/components/viz/service/gl/gpu_service_impl.h
+++ b/components/viz/service/gl/gpu_service_impl.h
@@ -165,6 +165,7 @@
   void EstablishGpuChannel(int32_t client_id,
                            uint64_t client_tracing_id,
                            bool is_gpu_host,
+                           bool enable_extra_handles_validation,
                            EstablishGpuChannelCallback callback) override;
   void SetChannelClientPid(int32_t client_id,
                            base::ProcessId client_pid) override;
diff --git a/components/viz/test/overlay_candidate_matchers.cc b/components/viz/test/overlay_candidate_matchers.cc
index bd57c4d..a6895ecc 100644
--- a/components/viz/test/overlay_candidate_matchers.cc
+++ b/components/viz/test/overlay_candidate_matchers.cc
@@ -111,6 +111,13 @@
                            ApproximatelyEquals(expected));
 }
 
+size_t NumOverlaysExcludingPrimaryPlane(
+    const OverlayCandidateList& candidate_list) {
+  return std::ranges::count_if(candidate_list, [](const auto& overlay) {
+    return !overlay.is_root_render_pass;
+  });
+}
+
 }  // namespace test
 
 }  // namespace viz
diff --git a/components/viz/test/overlay_candidate_matchers.h b/components/viz/test/overlay_candidate_matchers.h
index cbc5c1b..217aa14 100644
--- a/components/viz/test/overlay_candidate_matchers.h
+++ b/components/viz/test/overlay_candidate_matchers.h
@@ -51,6 +51,10 @@
 testing::Matcher<const OverlayCandidate&> OverlayTargetRectIs(
     const gfx::RectF& expected);
 
+// Return the count of non-primary-plane overlays in `candidate_list`.
+size_t NumOverlaysExcludingPrimaryPlane(
+    const OverlayCandidateList& candidate_list);
+
 }  // namespace test
 
 }  // namespace viz
diff --git a/components/viz/test/stub_gpu_service.cc b/components/viz/test/stub_gpu_service.cc
index a173b4d..ac633d95 100644
--- a/components/viz/test/stub_gpu_service.cc
+++ b/components/viz/test/stub_gpu_service.cc
@@ -14,6 +14,7 @@
 void StubGpuService::EstablishGpuChannel(int32_t client_id,
                                          uint64_t client_tracing_id,
                                          bool is_gpu_host,
+                                         bool enable_extra_handles_validation,
                                          EstablishGpuChannelCallback callback) {
 }
 
diff --git a/components/viz/test/stub_gpu_service.h b/components/viz/test/stub_gpu_service.h
index 74d45af..4a3ce51c 100644
--- a/components/viz/test/stub_gpu_service.h
+++ b/components/viz/test/stub_gpu_service.h
@@ -26,6 +26,7 @@
   void EstablishGpuChannel(int32_t client_id,
                            uint64_t client_tracing_id,
                            bool is_gpu_host,
+                           bool enable_extra_handles_validation,
                            EstablishGpuChannelCallback callback) override;
   void SetChannelClientPid(int32_t client_id,
                            base::ProcessId client_pid) override;
diff --git a/components/webauthn/android/BUILD.gn b/components/webauthn/android/BUILD.gn
index ac2e478..20414b8 100644
--- a/components/webauthn/android/BUILD.gn
+++ b/components/webauthn/android/BUILD.gn
@@ -38,6 +38,7 @@
     "java/src/org/chromium/components/webauthn/AuthenticatorErrorResponseCallback.java",
     "java/src/org/chromium/components/webauthn/AuthenticatorFactory.java",
     "java/src/org/chromium/components/webauthn/AuthenticatorImpl.java",
+    "java/src/org/chromium/components/webauthn/AuthenticatorReportResponseCallback.java",
     "java/src/org/chromium/components/webauthn/Barrier.java",
     "java/src/org/chromium/components/webauthn/CreateConfirmationUiDelegate.java",
     "java/src/org/chromium/components/webauthn/Fido2Api.java",
diff --git a/components/webauthn/android/fido2credentialrequest_native_android.cc b/components/webauthn/android/fido2credentialrequest_native_android.cc
index a459fd8..053ec79 100644
--- a/components/webauthn/android/fido2credentialrequest_native_android.cc
+++ b/components/webauthn/android/fido2credentialrequest_native_android.cc
@@ -96,4 +96,12 @@
       env, webauthn::GetAssertionResponseFromValue, jjson);
 }
 
+static base::android::ScopedJavaLocalRef<jstring>
+JNI_Fido2CredentialRequest_ReportOptionsToJson(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& byte_buffer) {
+  return MojoClassToJSON<blink::mojom::PublicKeyCredentialReportOptions>(
+      env, byte_buffer);
+}
+
 }  // namespace webauthn
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java
index 2e1948cd..4eb00b2 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java
@@ -76,6 +76,7 @@
 
     private @Nullable MakeCredential_Response mMakeCredentialCallback;
     private @Nullable GetCredential_Response mGetCredentialCallback;
+    private @Nullable Report_Response mReportCallback;
     private @Nullable Fido2CredentialRequest mPendingFido2CredentialRequest;
     private final Set<Fido2CredentialRequest> mUnclosedFido2CredentialRequests = new HashSet<>();
 
@@ -256,7 +257,23 @@
 
     @Override
     public void report(PublicKeyCredentialReportOptions options, Report_Response callback) {
-        callback.call(AuthenticatorStatus.NOT_IMPLEMENTED, null);
+        log(TAG, "report");
+        if (!DeviceFeatureMap.isEnabled(DeviceFeatureList.WEBAUTHN_ANDROID_SIGNAL)) {
+            callback.call(AuthenticatorStatus.NOT_IMPLEMENTED, null);
+            return;
+        }
+
+        if (mIsOperationPending) {
+            callback.call(AuthenticatorStatus.PENDING_REQUEST, null);
+            return;
+        }
+
+        mReportCallback = callback;
+        mIsOperationPending = true;
+
+        mPendingFido2CredentialRequest = getFido2CredentialRequest();
+        mPendingFido2CredentialRequest.handleReportRequest(
+                options, assertNonNull(mOrigin), this::onReportComplete);
     }
 
     private boolean couldSupportConditionalMediation() {
@@ -307,16 +324,6 @@
         capabilities.add(
                 createWebAuthnClientCapability(AuthenticatorConstants.CAPABILITY_PPAA, true));
 
-        capabilities.add(
-                createWebAuthnClientCapability(
-                        AuthenticatorConstants.CAPABILITY_SIGNAL_ALL_ACCEPTED_CREDENTIALS, false));
-        capabilities.add(
-                createWebAuthnClientCapability(
-                        AuthenticatorConstants.CAPABILITY_SIGNAL_CURRENT_USER_DETAILS, false));
-        capabilities.add(
-                createWebAuthnClientCapability(
-                        AuthenticatorConstants.CAPABILITY_SIGNAL_UNKNOWN_CREDENTIAL, false));
-
         if (!couldSupportUvpaa()) {
             capabilities.add(
                     createWebAuthnClientCapability(
@@ -329,6 +336,16 @@
             capabilities.add(
                     createWebAuthnClientCapability(
                             AuthenticatorConstants.CAPABILITY_IMMEDIATE_GET, false));
+            capabilities.add(
+                    createWebAuthnClientCapability(
+                            AuthenticatorConstants.CAPABILITY_SIGNAL_ALL_ACCEPTED_CREDENTIALS,
+                            false));
+            capabilities.add(
+                    createWebAuthnClientCapability(
+                            AuthenticatorConstants.CAPABILITY_SIGNAL_CURRENT_USER_DETAILS, false));
+            capabilities.add(
+                    createWebAuthnClientCapability(
+                            AuthenticatorConstants.CAPABILITY_SIGNAL_UNKNOWN_CREDENTIAL, false));
             callback.call(capabilities.toArray(new WebAuthnClientCapability[0]));
             return;
         }
@@ -359,6 +376,25 @@
                                                             DeviceFeatureList
                                                                     .WEBAUTHN_IMMEDIATE_GET)
                                                     && isUvpaa));
+                            boolean signal_supported =
+                                    isUvpaa
+                                            && DeviceFeatureMap.isEnabled(
+                                                    DeviceFeatureList.WEBAUTHN_ANDROID_SIGNAL);
+                            capabilities.add(
+                                    createWebAuthnClientCapability(
+                                            AuthenticatorConstants
+                                                    .CAPABILITY_SIGNAL_ALL_ACCEPTED_CREDENTIALS,
+                                            signal_supported));
+                            capabilities.add(
+                                    createWebAuthnClientCapability(
+                                            AuthenticatorConstants
+                                                    .CAPABILITY_SIGNAL_CURRENT_USER_DETAILS,
+                                            signal_supported));
+                            capabilities.add(
+                                    createWebAuthnClientCapability(
+                                            AuthenticatorConstants
+                                                    .CAPABILITY_SIGNAL_UNKNOWN_CREDENTIAL,
+                                            signal_supported));
                             callback.call(capabilities.toArray(new WebAuthnClientCapability[0]));
                         });
     }
@@ -465,6 +501,15 @@
         cleanupRequest();
     }
 
+    public void onReportComplete(int status) {
+        log(TAG, "onReportComplete completed with status: " + status);
+        // In case mojo pipe is closed due to the page begin destroyed while waiting for response.
+        if (!mIsOperationPending || mReportCallback == null) return;
+
+        mReportCallback.call(status, null);
+        cleanupRequest();
+    }
+
     public void onError(Integer status) {
         log(TAG, "Request completed with error: " + status);
         // In case mojo pipe is closed due to the page begin destroyed while waiting for response.
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorReportResponseCallback.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorReportResponseCallback.java
new file mode 100644
index 0000000..d048f40
--- /dev/null
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorReportResponseCallback.java
@@ -0,0 +1,13 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.webauthn;
+
+import org.chromium.build.annotations.NullMarked;
+
+/** Callback interface for handling any errors from register or sign requests. */
+@NullMarked
+public interface AuthenticatorReportResponseCallback {
+    void onComplete(int status);
+}
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2ApiTestHelper.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2ApiTestHelper.java
index ace15f3..50ff6cc2 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2ApiTestHelper.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2ApiTestHelper.java
@@ -445,6 +445,7 @@
             "{serialized_make_request}";
     private static final String TEST_SERIALIZED_GET_ASSERTION_REQUEST_JSON =
             "{serialized_get_request}";
+    public static final String TEST_SERIALIZED_REPORT_REQUEST_JSON = "{serialized_report_request}";
 
     /**
      * Builds a test intent to be returned by a successful call to makeCredential.
@@ -813,6 +814,11 @@
                     public byte[] getCredentialResponseFromJson(String json) {
                         return TEST_SERIALIZED_CREDMAN_GET_CREDENTIAL_RESPONSE;
                     }
+
+                    @Override
+                    public String reportOptionsToJson(ByteBuffer serializedOptions) {
+                        return TEST_SERIALIZED_REPORT_REQUEST_JSON;
+                    }
                 };
         Fido2CredentialRequestJni.setInstanceForTesting(fido2CredentialRequestJni);
     }
@@ -871,6 +877,12 @@
             unblock();
         }
 
+        public void onReportOutcome(int status) {
+            assert mStatus == null;
+            mStatus = status;
+            unblock();
+        }
+
         public void onRequestOutcome(int outcome) {
             assert mOutcome == null;
             mOutcome = outcome;
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
index d8039dd4..b2cc0f57 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
@@ -42,6 +42,7 @@
 import org.chromium.blink.mojom.PaymentOptions;
 import org.chromium.blink.mojom.PublicKeyCredentialCreationOptions;
 import org.chromium.blink.mojom.PublicKeyCredentialDescriptor;
+import org.chromium.blink.mojom.PublicKeyCredentialReportOptions;
 import org.chromium.blink.mojom.PublicKeyCredentialRequestOptions;
 import org.chromium.blink.mojom.PublicKeyCredentialType;
 import org.chromium.blink.mojom.ResidentKeyRequirement;
@@ -813,6 +814,34 @@
                 });
     }
 
+    public void handleReportRequest(
+            PublicKeyCredentialReportOptions options,
+            Origin origin,
+            AuthenticatorReportResponseCallback callback) {
+        RenderFrameHost frameHost = mAuthenticationContextProvider.getRenderFrameHost();
+        assert frameHost != null;
+
+        if (options.unknownCredentialId == null
+                && options.allAcceptedCredentials == null
+                && options.currentUserDetails == null) {
+            callback.onComplete(AuthenticatorStatus.UNKNOWN_ERROR);
+            return;
+        }
+
+        frameHost.performReportWebAuthSecurityChecks(
+                options.relyingPartyId,
+                origin,
+                (results) -> {
+                    if (results.securityCheckResult != AuthenticatorStatus.SUCCESS) {
+                        callback.onComplete(results.securityCheckResult);
+                        return;
+                    }
+                    mIdentityCredentialsHelper.handleReportRequest(
+                            options, convertOriginToString(origin));
+                    callback.onComplete(AuthenticatorStatus.SUCCESS);
+                });
+    }
+
     public void handleGetMatchingCredentialIdsRequest(
             String relyingPartyId,
             byte[][] allowCredentialIds,
@@ -1600,5 +1629,7 @@
         String getOptionsToJson(ByteBuffer serializedOptions);
 
         byte @Nullable [] getCredentialResponseFromJson(String json);
+
+        String reportOptionsToJson(ByteBuffer serializedOptions);
     }
 }
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/IdentityCredentialsHelper.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/IdentityCredentialsHelper.java
index 0797cd2..0c8224765 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/IdentityCredentialsHelper.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/IdentityCredentialsHelper.java
@@ -16,12 +16,14 @@
 import com.google.android.gms.identitycredentials.CreateCredentialRequest;
 import com.google.android.gms.identitycredentials.IdentityCredentialClient;
 import com.google.android.gms.identitycredentials.IdentityCredentialManager;
+import com.google.android.gms.identitycredentials.SignalCredentialStateRequest;
 
 import org.jni_zero.JNINamespace;
 
 import org.chromium.blink.mojom.AuthenticatorStatus;
 import org.chromium.blink.mojom.MakeCredentialAuthenticatorResponse;
 import org.chromium.blink.mojom.PublicKeyCredentialCreationOptions;
+import org.chromium.blink.mojom.PublicKeyCredentialReportOptions;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.components.webauthn.cred_man.CredManHelper;
@@ -118,6 +120,45 @@
         return credentialData;
     }
 
+    // Dispatches a Report request.
+    public void handleReportRequest(PublicKeyCredentialReportOptions options, String origin) {
+        log(TAG, "handleReportRequest");
+        try {
+            IdentityCredentialClient client =
+                    IdentityCredentialManager.Companion.getClient(
+                            assertNonNull(mAuthenticationContextProvider.getContext()));
+            client.signalCredentialState(buildSignalCredentialStateRequest(options, origin))
+                    .addOnSuccessListener(
+                            (handle) -> log(TAG, "Signal API request completed successfully"))
+                    .addOnFailureListener(
+                            (e) -> logError(TAG, "Signal API Report request failed ", e));
+        } catch (Exception e) {
+            logError(TAG, "handleReportRequest failed ", e);
+            return;
+        }
+    }
+
+    @VisibleForTesting
+    public SignalCredentialStateRequest buildSignalCredentialStateRequest(
+            PublicKeyCredentialReportOptions options, String origin) {
+        String type;
+        if (options.unknownCredentialId != null) {
+            type = CRED_MAN_PREFIX + "SIGNAL_UNKNOWN_CREDENTIAL_STATE_REQUEST_TYPE";
+        } else if (options.allAcceptedCredentials != null) {
+            type = CRED_MAN_PREFIX + "SIGNAL_ALL_ACCEPTED_CREDENTIALS_REQUEST_TYPE";
+        } else {
+            assert (options.currentUserDetails != null);
+            type = CRED_MAN_PREFIX + "SIGNAL_CURRENT_USER_DETAILS_STATE_REQUEST_TYPE";
+        }
+
+        String requestJson =
+                Fido2CredentialRequestJni.get().reportOptionsToJson(options.serialize());
+        Bundle requestDataBundle = new Bundle();
+        requestDataBundle.putCharSequence(CRED_MAN_PREFIX + "signal_request_json_key", requestJson);
+
+        return new SignalCredentialStateRequest(type, origin, requestDataBundle);
+    }
+
     @VisibleForTesting
     public CreateCredentialRequest buildConditionalCreateRequest(
             PublicKeyCredentialCreationOptions options,
diff --git a/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorImplTest.java b/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorImplTest.java
index b4b562d..c40cdc3f 100644
--- a/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorImplTest.java
+++ b/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorImplTest.java
@@ -46,7 +46,8 @@
 @SmallTest
 @EnableFeatures({
     DeviceFeatureList.WEBAUTHN_PASSKEY_UPGRADE,
-    DeviceFeatureList.WEBAUTHN_IMMEDIATE_GET
+    DeviceFeatureList.WEBAUTHN_IMMEDIATE_GET,
+    DeviceFeatureList.WEBAUTHN_ANDROID_SIGNAL
 })
 public class AuthenticatorImplTest {
     private AuthenticatorImpl mAuthenticator;
@@ -154,6 +155,14 @@
     }
 
     @Test
+    public void testGetClientCapabilities_SignalApi_Supported_WhenUvpaaAvailable() {
+        GmsCoreUtils.setGmsCoreVersionForTesting(GmsCoreUtils.GMSCORE_MIN_VERSION);
+        testCapability(AuthenticatorConstants.CAPABILITY_SIGNAL_ALL_ACCEPTED_CREDENTIALS, true);
+        testCapability(AuthenticatorConstants.CAPABILITY_SIGNAL_CURRENT_USER_DETAILS, true);
+        testCapability(AuthenticatorConstants.CAPABILITY_SIGNAL_UNKNOWN_CREDENTIAL, true);
+    }
+
+    @Test
     public void testGetClientCapabilities_Uvpaa_Supported() {
         GmsCoreUtils.setGmsCoreVersionForTesting(GmsCoreUtils.GMSCORE_MIN_VERSION);
         testCapability(AuthenticatorConstants.CAPABILITY_UVPAA, true);
diff --git a/components/webauthn/android/junit/src/org/chromium/components/webauthn/Fido2CredentialRequestRobolectricTest.java b/components/webauthn/android/junit/src/org/chromium/components/webauthn/Fido2CredentialRequestRobolectricTest.java
index 33663f98..7d3dde6 100644
--- a/components/webauthn/android/junit/src/org/chromium/components/webauthn/Fido2CredentialRequestRobolectricTest.java
+++ b/components/webauthn/android/junit/src/org/chromium/components/webauthn/Fido2CredentialRequestRobolectricTest.java
@@ -56,6 +56,7 @@
 import org.chromium.blink.mojom.Mediation;
 import org.chromium.blink.mojom.PublicKeyCredentialCreationOptions;
 import org.chromium.blink.mojom.PublicKeyCredentialDescriptor;
+import org.chromium.blink.mojom.PublicKeyCredentialReportOptions;
 import org.chromium.blink.mojom.PublicKeyCredentialRequestOptions;
 import org.chromium.blink.mojom.ResidentKeyRequirement;
 import org.chromium.blink_public.common.BlinkFeatures;
@@ -736,6 +737,16 @@
         verify(mBarrierMock).onFido2ApiCancelled(eq(AuthenticatorStatus.NOT_ALLOWED_ERROR));
     }
 
+    @Test
+    @SmallTest
+    public void testReportRequest_noSignalArgumentsSet_unknownError() {
+        PublicKeyCredentialReportOptions options = new PublicKeyCredentialReportOptions();
+        options.relyingPartyId = "rpId";
+        mRequest.handleReportRequest(options, mOrigin, mCallback::onReportOutcome);
+        assertThat(mCallback.getStatus())
+                .isEqualTo(Integer.valueOf(AuthenticatorStatus.UNKNOWN_ERROR));
+    }
+
     private void handleMakeCredentialRequest(Bundle browserOptions) {
         mRequest.handleMakeCredentialRequest(
                 mCreationOptions,
diff --git a/components/webauthn/android/junit/src/org/chromium/components/webauthn/IdentityCredentialsHelperRobolectricTest.java b/components/webauthn/android/junit/src/org/chromium/components/webauthn/IdentityCredentialsHelperRobolectricTest.java
index c436369..60cb0a6 100644
--- a/components/webauthn/android/junit/src/org/chromium/components/webauthn/IdentityCredentialsHelperRobolectricTest.java
+++ b/components/webauthn/android/junit/src/org/chromium/components/webauthn/IdentityCredentialsHelperRobolectricTest.java
@@ -14,6 +14,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.google.android.gms.identitycredentials.CreateCredentialRequest;
+import com.google.android.gms.identitycredentials.SignalCredentialStateRequest;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -22,7 +23,10 @@
 import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.blink.mojom.AllAcceptedCredentialsOptions;
+import org.chromium.blink.mojom.CurrentUserDetailsOptions;
 import org.chromium.blink.mojom.PublicKeyCredentialCreationOptions;
+import org.chromium.blink.mojom.PublicKeyCredentialReportOptions;
 import org.chromium.content_public.browser.RenderFrameHost;
 
 @RunWith(BaseRobolectricTestRunner.class)
@@ -50,6 +54,22 @@
         mHelper = new IdentityCredentialsHelper(mAuthenticationContextProviderMock);
     }
 
+    private void verifyBuildSignalCredentialStateRequest(
+            PublicKeyCredentialReportOptions options, String expectedType) {
+        SignalCredentialStateRequest request =
+                mHelper.buildSignalCredentialStateRequest(options, ORIGIN_STRING);
+
+        assertThat(request).isNotNull();
+        assertThat(request.getType()).isEqualTo(expectedType);
+        assertThat(request.getOrigin()).isEqualTo(ORIGIN_STRING);
+
+        Bundle expectedRequestData = new Bundle();
+        expectedRequestData.putString(
+                "androidx.credentials.signal_request_json_key",
+                Fido2ApiTestHelper.TEST_SERIALIZED_REPORT_REQUEST_JSON);
+        assertThat(request.getRequestData().toString()).isEqualTo(expectedRequestData.toString());
+    }
+
     @Test
     @SmallTest
     public void testBuildConditionalCreateRequest() {
@@ -97,4 +117,40 @@
         assertThat(request.getCandidateQueryData().toString())
                 .isEqualTo(expectedQueryData.toString());
     }
+
+    @Test
+    @SmallTest
+    public void testBuildSignalCredentialStateRequest_unknownCredentialId() {
+        PublicKeyCredentialReportOptions options = new PublicKeyCredentialReportOptions();
+        options.relyingPartyId = ORIGIN_STRING;
+        options.unknownCredentialId = new byte[] {1, 2, 3, 4};
+        verifyBuildSignalCredentialStateRequest(
+                options, "androidx.credentials.SIGNAL_UNKNOWN_CREDENTIAL_STATE_REQUEST_TYPE");
+    }
+
+    @Test
+    @SmallTest
+    public void testBuildSignalCredentialStateRequest_allAcceptedCredentials() {
+        PublicKeyCredentialReportOptions options = new PublicKeyCredentialReportOptions();
+        options.relyingPartyId = ORIGIN_STRING;
+        options.allAcceptedCredentials = new AllAcceptedCredentialsOptions();
+        options.allAcceptedCredentials.userId = new byte[] {1, 2, 3, 4};
+        options.allAcceptedCredentials.allAcceptedCredentialsIds =
+                new byte[][] {{6, 5, 4}, {3, 2, 1}};
+        verifyBuildSignalCredentialStateRequest(
+                options, "androidx.credentials.SIGNAL_ALL_ACCEPTED_CREDENTIALS_REQUEST_TYPE");
+    }
+
+    @Test
+    @SmallTest
+    public void testBuildSignalCredentialStateRequest_currentUserDetails() {
+        PublicKeyCredentialReportOptions options = new PublicKeyCredentialReportOptions();
+        options.relyingPartyId = ORIGIN_STRING;
+        options.currentUserDetails = new CurrentUserDetailsOptions();
+        options.currentUserDetails.userId = new byte[] {1, 2, 3, 4};
+        options.currentUserDetails.name = "username";
+        options.currentUserDetails.displayName = "displayName";
+        verifyBuildSignalCredentialStateRequest(
+                options, "androidx.credentials.SIGNAL_CURRENT_USER_DETAILS_STATE_REQUEST_TYPE");
+    }
 }
diff --git a/components/webauthn/json/value_conversions.cc b/components/webauthn/json/value_conversions.cc
index aee6b9a..fc86009 100644
--- a/components/webauthn/json/value_conversions.cc
+++ b/components/webauthn/json/value_conversions.cc
@@ -369,6 +369,29 @@
   return base::Value(std::move(ret));
 }
 
+base::Value::Dict ToValue(
+    const blink::mojom::AllAcceptedCredentialsOptions& options) {
+  base::Value::Dict value;
+  value.Set("userId", Base64UrlEncode(options.user_id));
+
+  base::Value::List accepted_credential_ids;
+  accepted_credential_ids.reserve(options.all_accepted_credentials_ids.size());
+  for (const auto& credential_id : options.all_accepted_credentials_ids) {
+    accepted_credential_ids.Append(Base64UrlEncode(credential_id));
+  }
+  value.Set("allAcceptedCredentialIds", std::move(accepted_credential_ids));
+  return value;
+}
+
+base::Value::Dict ToValue(
+    const blink::mojom::CurrentUserDetailsOptions& options) {
+  base::Value::Dict value;
+  value.Set("userId", Base64UrlEncode(options.user_id));
+  value.Set("name", options.name);
+  value.Set("displayName", options.display_name);
+  return value;
+}
+
 }  // namespace
 
 base::Value ToValue(
@@ -569,6 +592,25 @@
   return base::Value(std::move(value));
 }
 
+base::Value ToValue(
+    const blink::mojom::PublicKeyCredentialReportOptionsPtr& options) {
+  if (options->all_accepted_credentials) {
+    base::Value::Dict value = ToValue(*options->all_accepted_credentials);
+    value.Set("rpId", options->relying_party_id);
+    return base::Value(std::move(value));
+  } else if (options->current_user_details) {
+    base::Value::Dict value = ToValue(*options->current_user_details);
+    value.Set("rpId", options->relying_party_id);
+    return base::Value(std::move(value));
+  } else if (options->unknown_credential_id) {
+    base::Value::Dict value;
+    value.Set("rpId", options->relying_party_id);
+    value.Set("credentialId", Base64UrlEncode(*options->unknown_credential_id));
+    return base::Value(std::move(value));
+  }
+  NOTREACHED();
+}
+
 std::optional<blink::mojom::PRFValuesPtr> ParsePRFResults(
     const base::Value::Dict* results) {
   const std::optional<std::string> first =
diff --git a/components/webauthn/json/value_conversions.h b/components/webauthn/json/value_conversions.h
index 91f285d..7d179813 100644
--- a/components/webauthn/json/value_conversions.h
+++ b/components/webauthn/json/value_conversions.h
@@ -26,6 +26,15 @@
 base::Value ToValue(
     const blink::mojom::PublicKeyCredentialRequestOptionsPtr& options);
 
+// Converts a `PublicKeyCredentialReportOptions` into a `base::Value`.
+//
+// This is not formally specified, but corresponds to the JSON expected in
+// `requestJSON` field in `SignalCredentialStateRequest` in the Android
+// Credentials API.
+// https://developer.android.com/reference/androidx/credentials/SignalCredentialStateRequest
+base::Value ToValue(
+    const blink::mojom::PublicKeyCredentialReportOptionsPtr& options);
+
 // Converts a `base::Value` encoding a `PublicKeyCredential` instance from a
 // WebAuthn `get()` request into a `MakeCredentialAuthenticatorResponse`.
 // Returns a pair of the converted message and an error string. The message will
diff --git a/components/webauthn/json/value_conversions_unittest.cc b/components/webauthn/json/value_conversions_unittest.cc
index 6fa875fe..5079148f 100644
--- a/components/webauthn/json/value_conversions_unittest.cc
+++ b/components/webauthn/json/value_conversions_unittest.cc
@@ -45,6 +45,7 @@
 using blink::mojom::MakeCredentialAuthenticatorResponsePtr;
 using blink::mojom::PublicKeyCredentialCreationOptions;
 using blink::mojom::PublicKeyCredentialCreationOptionsPtr;
+using blink::mojom::PublicKeyCredentialReportOptions;
 using blink::mojom::PublicKeyCredentialRequestOptions;
 using blink::mojom::PublicKeyCredentialRequestOptionsPtr;
 using blink::mojom::RemoteDesktopClientOverride;
@@ -604,5 +605,50 @@
   }
 }
 
+TEST(WebAuthenticationJSONConversionTest,
+     PublicKeyCredentialReportOptionsToValue) {
+  {
+    // CurrentUserDetails
+    auto current_user_details = blink::mojom::CurrentUserDetailsOptions::New(
+        kUserId, kUserName, kUserDisplayName);
+    auto options = PublicKeyCredentialReportOptions::New(
+        kRpId, std::nullopt, nullptr, std::move(current_user_details));
+    base::Value value = ToValue(options);
+    std::string json;
+    JSONStringValueSerializer serializer(&json);
+    ASSERT_TRUE(serializer.Serialize(value));
+    EXPECT_EQ(
+        json,
+        R"({"displayName":"Example User","name":"user@example.test","rpId":"example.test","userId":"dGVzdCB1c2VyIGlk"})");
+  }
+  {
+    // AllAcceptedCredentials
+    std::vector<std::vector<uint8_t>> all_accepted_credentials_ids = {kUserId};
+    auto all_accepted_credentials =
+        blink::mojom::AllAcceptedCredentialsOptions::New(
+            kUserId, std::move(all_accepted_credentials_ids));
+    auto options = PublicKeyCredentialReportOptions::New(
+        kRpId, std::nullopt, std::move(all_accepted_credentials), nullptr);
+    base::Value value = ToValue(options);
+    std::string json;
+    JSONStringValueSerializer serializer(&json);
+    ASSERT_TRUE(serializer.Serialize(value));
+    EXPECT_EQ(
+        json,
+        R"({"allAcceptedCredentialIds":["dGVzdCB1c2VyIGlk"],"rpId":"example.test","userId":"dGVzdCB1c2VyIGlk"})");
+  }
+  {
+    // UnknownCredentialId
+    auto options =
+        PublicKeyCredentialReportOptions::New(kRpId, kUserId, nullptr, nullptr);
+    base::Value value = ToValue(options);
+    std::string json;
+    JSONStringValueSerializer serializer(&json);
+    ASSERT_TRUE(serializer.Serialize(value));
+    EXPECT_EQ(json,
+              R"({"credentialId":"dGVzdCB1c2VyIGlk","rpId":"example.test"})");
+  }
+}
+
 }  // namespace
 }  // namespace webauthn
diff --git a/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.h b/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.h
index 428902f..13e928e 100644
--- a/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.h
+++ b/content/app_shim_remote_cocoa/web_contents_occlusion_checker_mac.h
@@ -10,7 +10,6 @@
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
 #import "content/app_shim_remote_cocoa/web_contents_view_cocoa.h"
-#include "content/common/web_contents_ns_view_bridge.mojom.h"
 
 extern CONTENT_EXPORT const base::FeatureParam<bool>
     kDisplaySleepAndAppHideDetection;
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 8e7bb5b..160bdab 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -4817,6 +4817,11 @@
 }
 
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeWithCarouselTest,
+                       CarouselWithTabsDynamic) {
+  RunCSSTest(FILE_PATH_LITERAL("carousel-with-tabs-dynamic.html"));
+}
+
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeWithCarouselTest,
                        CarouselWithLinks) {
   RunCSSTest(FILE_PATH_LITERAL("carousel-with-links.html"));
 }
diff --git a/content/browser/android/app_web_message_port.h b/content/browser/android/app_web_message_port.h
index dd990c91..5057d24 100644
--- a/content/browser/android/app_web_message_port.h
+++ b/content/browser/android/app_web_message_port.h
@@ -11,8 +11,8 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/task/single_thread_task_runner.h"
 #include "content/common/content_export.h"
+#include "mojo/public/cpp/bindings/message.h"
 #include "third_party/blink/public/common/messaging/message_port_descriptor.h"
-#include "third_party/blink/public/mojom/messaging/transferable_message.mojom.h"
 
 namespace mojo {
 class Connector;
diff --git a/content/browser/android/javascript_injector.h b/content/browser/android/javascript_injector.h
index 624d172..d24de6d 100644
--- a/content/browser/android/javascript_injector.h
+++ b/content/browser/android/javascript_injector.h
@@ -5,14 +5,10 @@
 #ifndef CONTENT_BROWSER_ANDROID_JAVASCRIPT_INJECTOR_H_
 #define CONTENT_BROWSER_ANDROID_JAVASCRIPT_INJECTOR_H_
 
-#include <string>
-#include <vector>
-
 #include "base/android/jni_android.h"
 #include "base/android/jni_weak_ref.h"
 #include "base/android/scoped_java_ref.h"
 #include "components/origin_matcher/origin_matcher.h"
-#include "content/common/gin_java_bridge.mojom-forward.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 
diff --git a/content/browser/android/synchronous_compositor_host.cc b/content/browser/android/synchronous_compositor_host.cc
index 644bec0..43a6ba4 100644
--- a/content/browser/android/synchronous_compositor_host.cc
+++ b/content/browser/android/synchronous_compositor_host.cc
@@ -68,6 +68,18 @@
 
 }  // namespace
 
+struct SynchronousCompositorHost::SharedMemoryWithSize {
+  base::WritableSharedMemoryMapping shared_memory;
+  const size_t stride;
+  const size_t buffer_size;
+
+  SharedMemoryWithSize(size_t stride, size_t buffer_size)
+      : stride(stride), buffer_size(buffer_size) {}
+
+  SharedMemoryWithSize(const SharedMemoryWithSize&) = delete;
+  SharedMemoryWithSize& operator=(const SharedMemoryWithSize&) = delete;
+};
+
 // This class runs on the IO thread and is destroyed when the renderer
 // side closes the mojo channel.
 class SynchronousCompositorControlHost
@@ -374,18 +386,6 @@
   const raw_ptr<SynchronousCompositorHost> host_;
 };
 
-struct SynchronousCompositorHost::SharedMemoryWithSize {
-  base::WritableSharedMemoryMapping shared_memory;
-  const size_t stride;
-  const size_t buffer_size;
-
-  SharedMemoryWithSize(size_t stride, size_t buffer_size)
-      : stride(stride), buffer_size(buffer_size) {}
-
-  SharedMemoryWithSize(const SharedMemoryWithSize&) = delete;
-  SharedMemoryWithSize& operator=(const SharedMemoryWithSize&) = delete;
-};
-
 bool SynchronousCompositorHost::DemandDrawSw(SkCanvas* canvas,
                                              bool software_canvas) {
   velocity_in_pixels_per_second_ = 0.f;
diff --git a/content/browser/attribution_reporting/attribution_resolver_impl.h b/content/browser/attribution_reporting/attribution_resolver_impl.h
index 2b839383..d9851871 100644
--- a/content/browser/attribution_reporting/attribution_resolver_impl.h
+++ b/content/browser/attribution_reporting/attribution_resolver_impl.h
@@ -17,7 +17,6 @@
 #include "base/thread_annotations.h"
 #include "base/time/time.h"
 #include "base/types/expected.h"
-#include "content/browser/attribution_reporting/aggregatable_result.mojom-forward.h"
 #include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_resolver.h"
 #include "content/browser/attribution_reporting/attribution_storage_sql.h"
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc
index c9a73853..c8f7001c 100644
--- a/content/browser/browser_child_process_host_impl.cc
+++ b/content/browser/browser_child_process_host_impl.cc
@@ -400,14 +400,8 @@
   }
 }
 
-void BrowserChildProcessHostImpl::OnBadMessageReceived(
-    const IPC::Message& message) {
-  std::string log_message = "Bad message received of type: ";
-  if (message.IsValid()) {
-    log_message += base::NumberToString(message.type());
-  } else {
-    log_message += "unknown";
-  }
+void BrowserChildProcessHostImpl::OnBadMessageReceived() {
+  std::string log_message = "Bad message received of type: unknown";
   TerminateOnBadMessageReceived(log_message);
 }
 
diff --git a/content/browser/browser_child_process_host_impl.h b/content/browser/browser_child_process_host_impl.h
index f98edecc..709bb5d 100644
--- a/content/browser/browser_child_process_host_impl.h
+++ b/content/browser/browser_child_process_host_impl.h
@@ -93,7 +93,7 @@
   const base::Process& GetProcess() override;
   void BindHostReceiver(mojo::GenericPendingReceiver receiver) override;
   void OnChannelConnected(int32_t peer_pid) override;
-  void OnBadMessageReceived(const IPC::Message& message) override;
+  void OnBadMessageReceived() override;
 
   // HistogramChildProcess implementation:
   void BindChildHistogramFetcherFactory(
diff --git a/content/browser/browser_plugin/browser_plugin_guest.h b/content/browser/browser_plugin/browser_plugin_guest.h
index 7f308302..1a74764 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.h
+++ b/content/browser/browser_plugin/browser_plugin_guest.h
@@ -12,7 +12,6 @@
 #include "content/public/browser/browser_plugin_guest_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "third_party/blink/public/mojom/choosers/popup_menu.mojom.h"
 
 namespace content {
 class RenderFrameHostImpl;
diff --git a/content/browser/child_process_host_impl.cc b/content/browser/child_process_host_impl.cc
index 945d5f5b..4d1ec7ae 100644
--- a/content/browser/child_process_host_impl.cc
+++ b/content/browser/child_process_host_impl.cc
@@ -286,8 +286,8 @@
   OnDisconnectedFromChildProcess();
 }
 
-void ChildProcessHostImpl::OnBadMessageReceived(const IPC::Message& message) {
-  delegate_->OnBadMessageReceived(message);
+void ChildProcessHostImpl::OnBadMessageReceived() {
+  delegate_->OnBadMessageReceived();
 }
 
 #if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
diff --git a/content/browser/child_process_host_impl.h b/content/browser/child_process_host_impl.h
index 8ef9d089..cc8993d 100644
--- a/content/browser/child_process_host_impl.h
+++ b/content/browser/child_process_host_impl.h
@@ -100,7 +100,7 @@
   // IPC::Listener methods:
   void OnChannelConnected(int32_t peer_pid) override;
   void OnChannelError() override;
-  void OnBadMessageReceived(const IPC::Message& message) override;
+  void OnBadMessageReceived() override;
 
   // Initializes the IPC channel and returns true on success. |channel_| must be
   // non-null.
diff --git a/content/browser/child_process_launcher_helper_android.cc b/content/browser/child_process_launcher_helper_android.cc
index cbdc389..02023a8 100644
--- a/content/browser/child_process_launcher_helper_android.cc
+++ b/content/browser/child_process_launcher_helper_android.cc
@@ -21,6 +21,7 @@
 #include "base/logging.h"
 #include "base/metrics/field_trial.h"
 #include "base/process/launch.h"
+#include "base/trace_event/trace_event.h"
 #include "content/browser/child_process_launcher.h"
 #include "content/browser/child_process_launcher_helper_posix.h"
 #include "content/browser/posix_file_descriptor_info_impl.h"
@@ -302,6 +303,10 @@
 void ChildProcessLauncherHelper::SetRenderProcessPriorityOnLauncherThread(
     base::Process process,
     const RenderProcessPriority& priority) {
+  TRACE_EVENT(
+      "content",
+      "ChildProcessLauncherHelper::SetRenderProcessPriorityOnLauncherThread",
+      "pid", process.Handle());
   JNIEnv* env = AttachCurrentThread();
   DCHECK(env);
   Java_ChildProcessLauncherHelperImpl_setPriority(
diff --git a/content/browser/compositor/viz_process_transport_factory.h b/content/browser/compositor/viz_process_transport_factory.h
index 4de5805..85d4bb44 100644
--- a/content/browser/compositor/viz_process_transport_factory.h
+++ b/content/browser/compositor/viz_process_transport_factory.h
@@ -14,8 +14,6 @@
 #include "content/browser/compositor/image_transport_factory.h"
 #include "gpu/command_buffer/common/context_result.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
-#include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.h"
-#include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom.h"
 #include "ui/compositor/compositor.h"
 
 namespace base {
diff --git a/content/browser/devtools/dedicated_worker_devtools_agent_host.h b/content/browser/devtools/dedicated_worker_devtools_agent_host.h
index ee22d07c..314625a 100644
--- a/content/browser/devtools/dedicated_worker_devtools_agent_host.h
+++ b/content/browser/devtools/dedicated_worker_devtools_agent_host.h
@@ -6,7 +6,6 @@
 #define CONTENT_BROWSER_DEVTOOLS_DEDICATED_WORKER_DEVTOOLS_AGENT_HOST_H_
 
 #include "content/browser/devtools/worker_or_worklet_devtools_agent_host.h"
-#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 
 namespace blink {
 class StorageKey;
diff --git a/content/browser/devtools/devtools_agent_host_impl.h b/content/browser/devtools/devtools_agent_host_impl.h
index 4e783b95..9ee4f70 100644
--- a/content/browser/devtools/devtools_agent_host_impl.h
+++ b/content/browser/devtools/devtools_agent_host_impl.h
@@ -23,6 +23,7 @@
 #include "services/network/public/cpp/cross_origin_embedder_policy.h"
 #include "services/network/public/cpp/cross_origin_opener_policy.h"
 #include "services/network/public/mojom/content_security_policy.mojom.h"
+#include "services/network/public/mojom/network_context.mojom-forward.h"
 
 namespace content {
 
diff --git a/content/browser/devtools/devtools_instrumentation.cc b/content/browser/devtools/devtools_instrumentation.cc
index 6379b80..703b2708 100644
--- a/content/browser/devtools/devtools_instrumentation.cc
+++ b/content/browser/devtools/devtools_instrumentation.cc
@@ -874,6 +874,7 @@
 void DidUpdatePrerenderStatus(
     FrameTreeNodeId initiator_frame_tree_node_id,
     const base::UnguessableToken& initiator_devtools_navigation_token,
+    blink::mojom::SpeculationAction action,
     const GURL& prerender_url,
     std::optional<blink::mojom::SpeculationTargetHint> target_hint,
     const base::UnguessableToken& preload_pipeline_id,
@@ -897,11 +898,11 @@
   // We update DevToolsPreloadStorage, even if there are no active DevTools
   // sessions, to persist the latest status update.
   devtools_preload_storage->UpdatePrerenderStatus(
-      prerender_url, target_hint, preload_pipeline_id, status, prerender_status,
-      disallowed_mojo_interface, mismatched_headers);
+      action, prerender_url, target_hint, preload_pipeline_id, status,
+      prerender_status, disallowed_mojo_interface, mismatched_headers);
 
   DispatchToAgents(ftn, &protocol::PreloadHandler::DidUpdatePrerenderStatus,
-                   initiator_devtools_navigation_token, prerender_url,
+                   initiator_devtools_navigation_token, action, prerender_url,
                    target_hint, preload_pipeline_id, status, prerender_status,
                    disallowed_mojo_interface, mismatched_headers);
 }
diff --git a/content/browser/devtools/devtools_instrumentation.h b/content/browser/devtools/devtools_instrumentation.h
index c42b9e1..71640e0 100644
--- a/content/browser/devtools/devtools_instrumentation.h
+++ b/content/browser/devtools/devtools_instrumentation.h
@@ -312,6 +312,7 @@
 void DidUpdatePrerenderStatus(
     FrameTreeNodeId initiator_frame_tree_node_id,
     const base::UnguessableToken& initiator_devtools_navigation_token,
+    blink::mojom::SpeculationAction action,
     const GURL& prerender_url,
     std::optional<blink::mojom::SpeculationTargetHint> target_hint,
     const base::UnguessableToken& preload_pipeline_id,
diff --git a/content/browser/devtools/devtools_preload_storage.cc b/content/browser/devtools/devtools_preload_storage.cc
index 55a8174a..3a81d3a 100644
--- a/content/browser/devtools/devtools_preload_storage.cc
+++ b/content/browser/devtools/devtools_preload_storage.cc
@@ -40,6 +40,7 @@
 }
 
 void DevToolsPreloadStorage::UpdatePrerenderStatus(
+    blink::mojom::SpeculationAction action,
     const GURL& prerender_url,
     std::optional<blink::mojom::SpeculationTargetHint> target_hint,
     const base::UnguessableToken& preload_pipeline_id,
@@ -56,13 +57,25 @@
   if (mismatched_headers) {
     data.mismatched_headers = *mismatched_headers;
   }
-  prerender_data_map_[key] = std::move(data);
+
+  switch (action) {
+    case blink::mojom::SpeculationAction::kPrerender:
+      prerender_data_map_[key] = std::move(data);
+      break;
+    case blink::mojom::SpeculationAction::kPrerenderUntilScript:
+      prerender_until_script_data_map_[key] = std::move(data);
+      break;
+    case blink::mojom::SpeculationAction::kPrefetch:
+    case blink::mojom::SpeculationAction::kPrefetchWithSubresources:
+      NOTREACHED();
+  }
 }
 
 void DevToolsPreloadStorage::SpeculationCandidatesUpdated(
     const std::vector<blink::mojom::SpeculationCandidatePtr>& candidates) {
   std::set<PrefetchKey> prefetch_keys_from_candidates;
   std::set<PrerenderKey> prerender_keys_from_candidates;
+  std::set<PrerenderKey> prerender_until_script_keys_from_candidates;
 
   for (const auto& candidate_ptr : candidates) {
     switch (candidate_ptr->action) {
@@ -74,9 +87,12 @@
             std::make_pair(candidate_ptr->url,
                            candidate_ptr->target_browsing_context_name_hint));
         break;
-      case blink::mojom::SpeculationAction::kPrefetchWithSubresources:
-      // TODO(https://crbug.com/428500219): Implement this.
       case blink::mojom::SpeculationAction::kPrerenderUntilScript:
+        prerender_until_script_keys_from_candidates.insert(
+            std::make_pair(candidate_ptr->url,
+                           candidate_ptr->target_browsing_context_name_hint));
+        break;
+      case blink::mojom::SpeculationAction::kPrefetchWithSubresources:
         NOTIMPLEMENTED_LOG_ONCE();
     };
   }
@@ -87,6 +103,9 @@
   std::erase_if(prerender_data_map_, [&](const auto& pair) {
     return !prerender_keys_from_candidates.contains(pair.first);
   });
+  std::erase_if(prerender_until_script_data_map_, [&](const auto& pair) {
+    return !prerender_until_script_keys_from_candidates.contains(pair.first);
+  });
 }
 
 DevToolsPreloadStorage::DevToolsPreloadStorage(RenderFrameHost* rfh)
diff --git a/content/browser/devtools/devtools_preload_storage.h b/content/browser/devtools/devtools_preload_storage.h
index e9494b04..643d28ff 100644
--- a/content/browser/devtools/devtools_preload_storage.h
+++ b/content/browser/devtools/devtools_preload_storage.h
@@ -29,6 +29,7 @@
                             const std::string& request_id);
 
   void UpdatePrerenderStatus(
+      blink::mojom::SpeculationAction action,
       const GURL& prerender_url,
       std::optional<blink::mojom::SpeculationTargetHint>,
       const base::UnguessableToken& preload_pipeline_id,
@@ -65,6 +66,9 @@
   };
   using PrerenderDataMap = std::map<PrerenderKey, PrerenderData>;
   const PrerenderDataMap& prerender_data_map() { return prerender_data_map_; }
+  const PrerenderDataMap& prerender_until_script_data_map() {
+    return prerender_until_script_data_map_;
+  }
 
  private:
   explicit DevToolsPreloadStorage(RenderFrameHost* rfh);
@@ -74,6 +78,7 @@
 
   PrefetchDataMap prefetch_data_map_;
   PrerenderDataMap prerender_data_map_;
+  PrerenderDataMap prerender_until_script_data_map_;
 };
 
 }  // namespace content
diff --git a/content/browser/devtools/protocol/browser_handler.cc b/content/browser/devtools/protocol/browser_handler.cc
index cc2cac4f..bf8df7c 100644
--- a/content/browser/devtools/protocol/browser_handler.cc
+++ b/content/browser/devtools/protocol/browser_handler.cc
@@ -390,7 +390,7 @@
     std::unique_ptr<protocol::Browser::PermissionDescriptor> permission,
     const protocol::Browser::PermissionSetting& setting,
     std::optional<std::string> origin,
-    std::optional<std::string> embedding_origin,
+    std::optional<std::string> embedded_origin,
     std::optional<std::string> browser_context_id,
     std::unique_ptr<protocol::Browser::Backend::SetPermissionCallback>
         callback) {
@@ -420,25 +420,21 @@
   PermissionControllerImpl* permission_controller =
       PermissionControllerImpl::FromBrowserContext(browser_context);
 
-  std::optional<url::Origin> overridden_requesting_origin;
   std::optional<url::Origin> overridden_embedding_origin;
+  std::optional<url::Origin> overridden_requesting_origin;
   if (origin.has_value()) {
-    ASSIGN_OR_RETURN(overridden_requesting_origin, ParseOriginString(origin),
-                     [&callback](Response response) {
-                       callback->sendFailure(response);
-                       return;
-                     });
+    ASSIGN_OR_RETURN(
+        overridden_embedding_origin, ParseOriginString(origin),
+        [&callback](Response response) { callback->sendFailure(response); });
 
-    // Only consider the embedding origin if there's a requesting origin. Use
-    // the requesting origin as the embedding origin, if one is not provided.
-    ASSIGN_OR_RETURN(overridden_embedding_origin,
-                     ParseOriginString(embedding_origin),
-                     [&callback](Response response) {
-                       callback->sendFailure(response);
-                       return;
-                     });
-    if (!overridden_embedding_origin) {
-      overridden_embedding_origin = overridden_requesting_origin;
+    // Only consider `embedded_origin` if `origin` is valid and non-nullopt. Use
+    // `origin` as `overridden_requesting_origin`, if `embedded_origin` is
+    // nullopt.
+    ASSIGN_OR_RETURN(
+        overridden_requesting_origin, ParseOriginString(embedded_origin),
+        [&callback](Response response) { callback->sendFailure(response); });
+    if (!overridden_requesting_origin) {
+      overridden_requesting_origin = overridden_embedding_origin;
     }
   }
 
diff --git a/content/browser/devtools/protocol/browser_handler.h b/content/browser/devtools/protocol/browser_handler.h
index 9dfbca6..916cdc7 100644
--- a/content/browser/devtools/protocol/browser_handler.h
+++ b/content/browser/devtools/protocol/browser_handler.h
@@ -68,7 +68,7 @@
       std::unique_ptr<protocol::Browser::PermissionDescriptor> permission,
       const protocol::Browser::PermissionSetting& setting,
       std::optional<std::string> origin,
-      std::optional<std::string> embedding_origin,
+      std::optional<std::string> embedded_origin,
       std::optional<std::string> browser_context_id,
       std::unique_ptr<protocol::Browser::Backend::SetPermissionCallback>
           callback) override;
diff --git a/content/browser/devtools/protocol/preload_handler.cc b/content/browser/devtools/protocol/preload_handler.cc
index 58b5d03..1104b6c3 100644
--- a/content/browser/devtools/protocol/preload_handler.cc
+++ b/content/browser/devtools/protocol/preload_handler.cc
@@ -354,6 +354,21 @@
   }
 }
 
+Preload::SpeculationAction SpeculationActionToProtocol(
+    blink::mojom::SpeculationAction action) {
+  switch (action) {
+    case blink::mojom::SpeculationAction::kPrerender:
+      return Preload::SpeculationActionEnum::Prerender;
+    case blink::mojom::SpeculationAction::kPrerenderUntilScript:
+      return Preload::SpeculationActionEnum::PrerenderUntilScript;
+    case blink::mojom::SpeculationAction::kPrefetch:
+      return Preload::SpeculationActionEnum::Prefetch;
+    case blink::mojom::SpeculationAction::kPrefetchWithSubresources:
+      // `kPrefetchWithSubresources` will be deprecated soon.
+      NOTREACHED();
+  }
+}
+
 }  // namespace
 
 PreloadHandler::PreloadHandler()
@@ -396,6 +411,7 @@
 
 void PreloadHandler::DidUpdatePrerenderStatus(
     const base::UnguessableToken& initiator_devtools_navigation_token,
+    blink::mojom::SpeculationAction action,
     const GURL& prerender_url,
     std::optional<blink::mojom::SpeculationTargetHint> target_hint,
     const base::UnguessableToken& preload_pipeline_id,
@@ -410,7 +426,7 @@
   auto preloading_attempt_key =
       protocol::Preload::PreloadingAttemptKey::Create()
           .SetLoaderId(initiator_devtools_navigation_token.ToString())
-          .SetAction(Preload::SpeculationActionEnum::Prerender)
+          .SetAction(SpeculationActionToProtocol(action))
           .SetUrl(prerender_url.spec())
           .Build();
   std::optional<protocol::Preload::SpeculationTargetHint> protocol_target_hint =
@@ -562,7 +578,19 @@
     }
     for (const auto& [key, data] : preload_storage->prerender_data_map()) {
       DidUpdatePrerenderStatus(
-          initiator_devtools_navigation_token, /*prerender_url=*/key.first,
+          initiator_devtools_navigation_token,
+          blink::mojom::SpeculationAction::kPrerender,
+          /*prerender_url=*/key.first,
+          /*target_hint=*/key.second, data.preload_pipeline_id, data.outcome,
+          data.status, data.disallowed_mojo_interface,
+          data.mismatched_headers.empty() ? nullptr : &data.mismatched_headers);
+    }
+    for (const auto& [key, data] :
+         preload_storage->prerender_until_script_data_map()) {
+      DidUpdatePrerenderStatus(
+          initiator_devtools_navigation_token,
+          blink::mojom::SpeculationAction::kPrerenderUntilScript,
+          /*prerender_url=*/key.first,
           /*target_hint=*/key.second, data.preload_pipeline_id, data.outcome,
           data.status, data.disallowed_mojo_interface,
           data.mismatched_headers.empty() ? nullptr : &data.mismatched_headers);
diff --git a/content/browser/devtools/protocol/preload_handler.h b/content/browser/devtools/protocol/preload_handler.h
index c4e59f1..3a70ad09 100644
--- a/content/browser/devtools/protocol/preload_handler.h
+++ b/content/browser/devtools/protocol/preload_handler.h
@@ -46,6 +46,7 @@
       const std::string& request_id);
   void DidUpdatePrerenderStatus(
       const base::UnguessableToken& initiator_devtools_navigation_token,
+      blink::mojom::SpeculationAction action,
       const GURL& prerender_url,
       std::optional<blink::mojom::SpeculationTargetHint> target_hint,
       const base::UnguessableToken& preload_pipeline_id,
diff --git a/content/browser/devtools/worker_devtools_manager.h b/content/browser/devtools/worker_devtools_manager.h
index 99c302c..486d559 100644
--- a/content/browser/devtools/worker_devtools_manager.h
+++ b/content/browser/devtools/worker_devtools_manager.h
@@ -12,7 +12,6 @@
 #include "content/browser/devtools/devtools_throttle_handle.h"
 #include "content/public/browser/devtools_agent_host.h"
 #include "content/public/browser/global_routing_id.h"
-#include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/digital_credentials/cross_device_transaction_impl.h b/content/browser/digital_credentials/cross_device_transaction_impl.h
index 2da62c6..82033473 100644
--- a/content/browser/digital_credentials/cross_device_transaction_impl.h
+++ b/content/browser/digital_credentials/cross_device_transaction_impl.h
@@ -21,7 +21,6 @@
 #include "device/fido/cable/v2_constants.h"
 #include "device/fido/cable/v2_discovery.h"
 #include "device/fido/network_context_factory.h"
-#include "services/network/public/mojom/network_context.mojom-forward.h"
 
 namespace device {
 class BluetoothAdapter;
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.cc b/content/browser/gpu/browser_gpu_channel_host_factory.cc
index 9f301c6..336bc2e 100644
--- a/content/browser/gpu/browser_gpu_channel_host_factory.cc
+++ b/content/browser/gpu/browser_gpu_channel_host_factory.cc
@@ -169,7 +169,8 @@
 
   bool is_gpu_host = true;
   host->gpu_host()->EstablishGpuChannel(
-      gpu_client_id_, gpu_client_tracing_id_, is_gpu_host, sync,
+      gpu_client_id_, gpu_client_tracing_id_, is_gpu_host,
+      /*enable_extra_handles_validation=*/false, sync,
       base::BindOnce(
           &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablished,
           this));
diff --git a/content/browser/gpu/gpu_client.cc b/content/browser/gpu/gpu_client.cc
index 9b7e9e6..668d9d9 100644
--- a/content/browser/gpu/gpu_client.cc
+++ b/content/browser/gpu/gpu_client.cc
@@ -12,7 +12,8 @@
 namespace content {
 
 std::unique_ptr<viz::GpuClient, base::OnTaskRunnerDeleter> CreateGpuClient(
-    mojo::PendingReceiver<viz::mojom::Gpu> receiver) {
+    mojo::PendingReceiver<viz::mojom::Gpu> receiver,
+    bool enable_extra_handles_validation) {
   // TODO(crbug.com/379869738): Refactor to use client_id without
   // GetUnsafeValue().
   const ChildProcessId client_id =
@@ -23,7 +24,7 @@
   std::unique_ptr<viz::GpuClient, base::OnTaskRunnerDeleter> gpu_client(
       new viz::GpuClient(std::make_unique<BrowserGpuClientDelegate>(),
                          client_id.GetUnsafeValue(), client_tracing_id,
-                         task_runner),
+                         enable_extra_handles_validation, task_runner),
       base::OnTaskRunnerDeleter(task_runner));
   task_runner->PostTask(
       FROM_HERE, base::BindOnce(&viz::GpuClient::Add, gpu_client->GetWeakPtr(),
diff --git a/content/browser/in_memory_federated_permission_context.h b/content/browser/in_memory_federated_permission_context.h
index 1648e63..33165af9 100644
--- a/content/browser/in_memory_federated_permission_context.h
+++ b/content/browser/in_memory_federated_permission_context.h
@@ -20,7 +20,6 @@
 #include "content/public/browser/webid/federated_identity_auto_reauthn_permission_context_delegate.h"
 #include "content/public/browser/webid/federated_identity_permission_context_delegate.h"
 #include "net/base/schemeful_site.h"
-#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom-forward.h"
 #include "url/gurl.h"
 
 namespace url {
diff --git a/content/browser/indexed_db/instance/transaction.cc b/content/browser/indexed_db/instance/transaction.cc
index ab4ced6..c2c8c4c7 100644
--- a/content/browser/indexed_db/instance/transaction.cc
+++ b/content/browser/indexed_db/instance/transaction.cc
@@ -1157,8 +1157,7 @@
   CHECK(!diagnostics_.mojo_receiver_disconnected, base::NotFatalUntil::M145);
   CHECK(task_queue_.empty(), base::NotFatalUntil::M145);
   CHECK(preemptive_task_queue_.empty(), base::NotFatalUntil::M145);
-  const bool has_connection = (connection_.get() != nullptr);
-  CHECK(has_connection, base::NotFatalUntil::M145);
+  CHECK(connection_.get() != nullptr);
 
   const size_t num_transactions_across_all_connections =
       database_->GetNumTransactionsAcrossAllConnections();
@@ -1174,17 +1173,11 @@
   base::UmaHistogramCounts10000(
       "IndexedDB.TransactionTimeout.NumTransactionsInDB",
       num_transactions_across_all_connections);
-
-  // Note: There is a non-fatal CHECK above the validates that `has_connection`
-  // is always true. There is a condition here to avoid a crash if the non-fatal
-  // CHECK fails.
-  if (has_connection) {
-    base::UmaHistogramBoolean("IndexedDB.TransactionTimeout.IsConnected",
-                              connection_->IsConnected());
-    base::UmaHistogramCounts10000(
-        "IndexedDB.TransactionTimeout.NumTransactionsInConnection",
-        connection_->transactions().size());
-  }
+  base::UmaHistogramBoolean("IndexedDB.TransactionTimeout.IsConnected",
+                            connection_->IsConnected());
+  base::UmaHistogramCounts10000(
+      "IndexedDB.TransactionTimeout.NumTransactionsInConnection",
+      connection_->transactions().size());
 
   // Same histograms as above, but only when there are a lot of transactions in
   // the connection.
@@ -1202,19 +1195,13 @@
     base::UmaHistogramCounts100000(
         "IndexedDB.TransactionTimeout.10kTransactions.NumTransactionsInDB",
         num_transactions_across_all_connections);
-
-    // Note: There is a non-fatal CHECK above the validates that
-    // `has_connection` is always true. There is a condition here to avoid a
-    // crash if the non-fatal CHECK fails.
-    if (has_connection) {
-      base::UmaHistogramBoolean(
-          "IndexedDB.TransactionTimeout.10kTransactions.IsConnected",
-          connection_->IsConnected());
-      base::UmaHistogramCounts100000(
-          "IndexedDB.TransactionTimeout.10kTransactions."
-          "NumTransactionsInConnection",
-          connection_->transactions().size());
-    }
+    base::UmaHistogramBoolean(
+        "IndexedDB.TransactionTimeout.10kTransactions.IsConnected",
+        connection_->IsConnected());
+    base::UmaHistogramCounts100000(
+        "IndexedDB.TransactionTimeout.10kTransactions."
+        "NumTransactionsInConnection",
+        connection_->transactions().size());
   }
 
   if (!IsTransactionBlockingOtherClients(/*consider_priority=*/true)) {
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index 0153d4f..a6be32a 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -94,6 +94,7 @@
 #include "content/public/test/content_browser_test_content_browser_client.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/privacy_sandbox_coordinator_test_util.h"
+#include "content/public/test/render_frame_host_test_support.h"
 #include "content/public/test/test_frame_navigation_observer.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/url_loader_monitor.h"
@@ -26360,7 +26361,7 @@
 // Test to make sure we don't crash when Page changes with DFSS ad slot pending.
 // https://crbug.com/326085515
 // TODO(crbug.com/446756531): Re-enable this test.
-#if (BUILDFLAG(IS_FUCHSIA) || (BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER)))
+#if BUILDFLAG(IS_FUCHSIA)
 #define MAYBE_PageImplChangeDirectFromSellerSignals \
   DISABLED_PageImplChangeDirectFromSellerSignals
 #else
@@ -26398,10 +26399,14 @@
   // 2) Act as if there was an infinite unload handler in the OOPIF.
   child_rfh->DoNotDeleteForTesting();
 
-  // Set an arbitrarily long timeout to ensure the subframe unload timer doesn't
-  // fire before we call OnDetach().
+  // Ensure the subframe unload timer and the unload timers don't fire before
+  // we call OnDetach().
+  DisableUnloadTimerForTesting(child_rfh);
   child_rfh->SetSubframeUnloadTimeoutForTesting(base::Seconds(30));
 
+  // ...and also for main.
+  DisableUnloadTimerForTesting(root_ftn->current_frame_host());
+
   // With BackForwardCache, old document doesn't fire unload handlers as the
   // page is stored in BackForwardCache on navigation.
   DisableBackForwardCacheForTesting(web_contents(),
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index e85bd7004..fc926778 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -636,7 +636,8 @@
   new_request->enable_load_timing = true;
 
   if (base::FeatureList::IsEnabled(
-          network::features::kRendererSideContentDecoding)) {
+          network::features::kRendererSideContentDecoding) &&
+      network::features::kRendererSideContentDecodingForNavigation.Get()) {
     new_request->client_side_content_decoding_enabled = true;
   }
 
diff --git a/content/browser/media/cdm_registry_impl.h b/content/browser/media/cdm_registry_impl.h
index 1b03b16..7baae41e2 100644
--- a/content/browser/media/cdm_registry_impl.h
+++ b/content/browser/media/cdm_registry_impl.h
@@ -5,9 +5,11 @@
 #ifndef CONTENT_BROWSER_MEDIA_CDM_REGISTRY_IMPL_H_
 #define CONTENT_BROWSER_MEDIA_CDM_REGISTRY_IMPL_H_
 
+#include <set>
 #include <vector>
 
 #include "base/callback_list.h"
+#include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/thread_annotations.h"
@@ -18,7 +20,6 @@
 #include "content/public/common/cdm_info.h"
 #include "media/base/cdm_capability.h"
 #include "media/base/key_system_capability.h"
-#include "media/mojo/mojom/key_system_support.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/media/oop_video_decoder_factory.cc b/content/browser/media/oop_video_decoder_factory.cc
index 1157641..2abfb8234 100644
--- a/content/browser/media/oop_video_decoder_factory.cc
+++ b/content/browser/media/oop_video_decoder_factory.cc
@@ -12,6 +12,7 @@
 #include "content/public/browser/gpu_data_manager_observer.h"
 #include "content/public/browser/gpu_utils.h"
 #include "content/public/browser/service_process_host.h"
+#include "content/public/browser/service_process_host_passkeys.h"
 #include "media/mojo/mojom/interface_factory.mojom.h"
 #include "media/mojo/mojom/video_decoder.mojom.h"
 #include "media/mojo/mojom/video_decoder_factory_process.mojom.h"
@@ -21,8 +22,6 @@
 
 #if BUILDFLAG(ALLOW_HOSTING_OOP_VIDEO_DECODER)
 
-namespace {
-
 // OOPVideoDecoderFactoryProcessLauncher is a helper singleton class that
 // launches utility processes to host a media::mojom::InterfaceFactory once
 // the gpu::GpuFeatureInfo is known.
@@ -117,9 +116,16 @@
     }
 
     mojo::Remote<media::mojom::VideoDecoderFactoryProcess> process;
+    // TODO(https://crbug.com/403183890): Instead allowing utility process to
+    // request a viz.mojom.Gpu pipe, directly pass a viz.mojom.Gpu
+    // pending_remote here. See crbug.com/328099369 for more information.
     ServiceProcessHost::Launch(
         process.BindNewPipeAndPassReceiver(),
-        ServiceProcessHost::Options().WithDisplayName("Video Decoder").Pass());
+        ServiceProcessHost::Options()
+            .WithDisplayName("Video Decoder")
+            .WithGpuClient(/*enable_extra_handles_validation=*/true,
+                           ServiceProcessHostGpuClient::GetPassKey())
+            .Pass());
     process->InitializeVideoDecoderFactory(*gpu_feature_info_,
                                            std::move(receiver));
     processes_.Add(std::move(process));
@@ -153,8 +159,6 @@
       pending_factory_receivers_ GUARDED_BY_CONTEXT(ui_sequence_checker_);
 };
 
-}  // namespace
-
 #endif  // BUILDFLAG(ALLOW_HOSTING_OOP_VIDEO_DECODER)
 
 void LaunchOOPVideoDecoderFactory(
diff --git a/content/browser/preloading/prefetch/prefetch_features.cc b/content/browser/preloading/prefetch/prefetch_features.cc
index 71361aa..882594b 100644
--- a/content/browser/preloading/prefetch/prefetch_features.cc
+++ b/content/browser/preloading/prefetch/prefetch_features.cc
@@ -86,7 +86,6 @@
         &kPrefetchMultipleActiveSetSizeLimitForBase,
         "prefetch_multiple_active_set_size_limit_for_base_value", 2};
 
-// Currently this feature is disabled for https://crbug.com/444634885.
-BASE_FEATURE(kPreloadServingMetrics, base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kPreloadServingMetrics, base::FEATURE_ENABLED_BY_DEFAULT);
 
 }  // namespace features
diff --git a/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.h b/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.h
index b903c28..d3def65 100644
--- a/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.h
+++ b/content/browser/preloading/prefetch/prefetch_url_loader_interceptor.h
@@ -13,7 +13,7 @@
 #include "content/browser/preloading/prefetch/prefetch_serving_handle.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/frame_tree_node_id.h"
-#include "third_party/blink/public/mojom/tokens/tokens.mojom.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 
 namespace content {
 
diff --git a/content/browser/preloading/preload_serving_metrics.cc b/content/browser/preloading/preload_serving_metrics.cc
index 4eff2a6..a1f2f4b 100644
--- a/content/browser/preloading/preload_serving_metrics.cc
+++ b/content/browser/preloading/preload_serving_metrics.cc
@@ -38,10 +38,21 @@
     const bool is_potential_match =
         meaningful_prefetch_match_metrics &&
         meaningful_prefetch_match_metrics->IsPotentialMatch();
+    const bool is_potential_match_with_ahead_of_prerender =
+        is_potential_match &&
+        meaningful_prefetch_match_metrics
+            ->prefetch_potential_candidate_serving_result_ahead_of_prerender
+            .has_value();
 
     base::UmaHistogramBoolean(
         WITH(prefix, "PrefetchMatchMetrics.IsPotentialMatch"),
         is_potential_match);
+    if (is_prerender_initial_navigation) {
+      base::UmaHistogramBoolean(
+          WITH(prefix,
+               "PrefetchMatchMetrics.IsPotentialMatch.WithAheadOfPrerender"),
+          is_potential_match_with_ahead_of_prerender);
+    }
 
     if (!is_potential_match) {
       return;
@@ -104,24 +115,17 @@
           time_from_prefetch_container_added_to_match_start);
     }
 
-    if (is_prerender_initial_navigation) {
-      base::UmaHistogramBoolean(
+    if (is_prerender_initial_navigation &&
+        prefetch_match_metrics
+            .prefetch_potential_candidate_serving_result_ahead_of_prerender
+            .has_value()) {
+      base::UmaHistogramEnumeration(
           WITH(prefix,
-               "PrefetchMatchMetrics.IsPotentialMatch.WithAheadOfPrerender"),
+               "PrefetchMatchMetrics.PotentialMatchThen.WithAheadOfPrerender."
+               "PotentialCandidateServingResult"),
           prefetch_match_metrics
               .prefetch_potential_candidate_serving_result_ahead_of_prerender
-              .has_value());
-      if (prefetch_match_metrics
-              .prefetch_potential_candidate_serving_result_ahead_of_prerender
-              .has_value()) {
-        base::UmaHistogramEnumeration(
-            WITH(prefix,
-                 "PrefetchMatchMetrics.PotentialMatchThen.WithAheadOfPrerender."
-                 "PotentialCandidateServingResult"),
-            prefetch_match_metrics
-                .prefetch_potential_candidate_serving_result_ahead_of_prerender
-                .value());
-      }
+              .value());
     }
   }();
 }
diff --git a/content/browser/preloading/prerender/devtools_prerender_attempt.cc b/content/browser/preloading/prerender/devtools_prerender_attempt.cc
index d0f7c7c..b73473c0 100644
--- a/content/browser/preloading/prerender/devtools_prerender_attempt.cc
+++ b/content/browser/preloading/prerender/devtools_prerender_attempt.cc
@@ -23,8 +23,9 @@
   devtools_instrumentation::DidUpdatePrerenderStatus(
       attributes.initiator_frame_tree_node_id,
       attributes.initiator_devtools_navigation_token.value(),
-      attributes.prerendering_url, attributes.GetTargetHint(),
-      attributes.preload_pipeline_info->id(), outcome,
+      attributes.prerender_action_type, attributes.prerendering_url,
+      attributes.GetTargetHint(), attributes.preload_pipeline_info->id(),
+      outcome,
       /*prerender_status=*/std::nullopt,
       /*disallowed_mojo_interface=*/std::nullopt,
       /*mismatched_headers=*/nullptr);
@@ -45,8 +46,8 @@
   devtools_instrumentation::DidUpdatePrerenderStatus(
       attributes.initiator_frame_tree_node_id,
       attributes.initiator_devtools_navigation_token.value(),
-      attributes.prerendering_url, attributes.GetTargetHint(),
-      attributes.preload_pipeline_info->id(),
+      attributes.prerender_action_type, attributes.prerendering_url,
+      attributes.GetTargetHint(), attributes.preload_pipeline_info->id(),
       PreloadingTriggeringOutcome::kFailure, prerender_status,
       /*disallowed_mojo_interface=*/std::nullopt,
       /*mismatched_headers=*/nullptr);
@@ -79,8 +80,8 @@
   devtools_instrumentation::DidUpdatePrerenderStatus(
       attributes.initiator_frame_tree_node_id,
       attributes.initiator_devtools_navigation_token.value(),
-      attributes.prerendering_url, attributes.GetTargetHint(),
-      attributes.preload_pipeline_info->id(),
+      attributes.prerender_action_type, attributes.prerendering_url,
+      attributes.GetTargetHint(), attributes.preload_pipeline_info->id(),
       PreloadingTriggeringOutcome::kFailure, prerender_status,
       disallowed_mojo_interface, mismatched_headers);
 }
diff --git a/content/browser/renderer_host/agent_scheduling_group_host.cc b/content/browser/renderer_host/agent_scheduling_group_host.cc
index 5fd1c99..ae0792ee 100644
--- a/content/browser/renderer_host/agent_scheduling_group_host.cc
+++ b/content/browser/renderer_host/agent_scheduling_group_host.cc
@@ -206,25 +206,10 @@
   SetState(LifecycleState::kRenderProcessHostDestroyed);
 }
 
-bool AgentSchedulingGroupHost::OnMessageReceived(const IPC::Message& message) {
-  if (message.routing_id() == MSG_ROUTING_CONTROL) {
-    bad_message::ReceivedBadMessage(&*process_,
-                                    bad_message::ASGH_RECEIVED_CONTROL_MESSAGE);
-    return false;
-  }
-
-  auto* listener = GetListener(message.routing_id());
-  if (!listener)
-    return false;
-
-  return listener->OnMessageReceived(message);
-}
-
-void AgentSchedulingGroupHost::OnBadMessageReceived(
-    const IPC::Message& message) {
+void AgentSchedulingGroupHost::OnBadMessageReceived() {
   // If a bad message is received, it should be treated the same as a bad
   // message on the renderer-wide channel (i.e., kill the renderer).
-  return process_->OnBadMessageReceived(message);
+  return process_->OnBadMessageReceived();
 }
 
 void AgentSchedulingGroupHost::OnAssociatedInterfaceRequest(
diff --git a/content/browser/renderer_host/agent_scheduling_group_host.h b/content/browser/renderer_host/agent_scheduling_group_host.h
index 5254cba..af49204 100644
--- a/content/browser/renderer_host/agent_scheduling_group_host.h
+++ b/content/browser/renderer_host/agent_scheduling_group_host.h
@@ -32,7 +32,6 @@
 
 namespace IPC {
 class ChannelProxy;
-class Message;
 }  // namespace IPC
 
 namespace content {
@@ -129,8 +128,7 @@
   friend std::ostream& operator<<(std::ostream& os, LifecycleState state);
 
   // IPC::Listener
-  bool OnMessageReceived(const IPC::Message& message) override;
-  void OnBadMessageReceived(const IPC::Message& message) override;
+  void OnBadMessageReceived() override;
   void OnAssociatedInterfaceRequest(
       const std::string& interface_name,
       mojo::ScopedInterfaceEndpointHandle handle) override;
diff --git a/content/browser/renderer_host/input/input_transfer_handler_android.cc b/content/browser/renderer_host/input/input_transfer_handler_android.cc
index 5b5c378..bc555da 100644
--- a/content/browser/renderer_host/input/input_transfer_handler_android.cc
+++ b/content/browser/renderer_host/input/input_transfer_handler_android.cc
@@ -4,7 +4,6 @@
 
 #include "content/browser/renderer_host/input/input_transfer_handler_android.h"
 
-#include "base/android/android_info.h"
 #include "base/android/jni_android.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
@@ -126,7 +125,7 @@
     TRACE_EVENT_INSTANT("input,input.scrolling", "DownTimeAfterEventTime");
     EmitTransferResultHistogramAndTraceEvent(
         TransferInputToVizResult::kDownTimeAfterEventTime);
-    if (!NewPointersGoDirectlyToViz() && active_touch_sequence_on_viz) {
+    if (active_touch_sequence_on_viz) {
       OnStartDroppingSequence(
           event,
           InputOnVizSequenceDroppedReason::kActiveSeqOnVizAbnormalDownTime);
@@ -172,10 +171,6 @@
     return true;
   }
 
-  if (NewPointersGoDirectlyToViz()) {
-    return false;
-  }
-
   if (!active_touch_sequence_on_viz) {
     return false;
   }
@@ -239,38 +234,6 @@
   last_seen_touch_end_ts_ = event_time;
 }
 
-bool InputTransferHandlerAndroid::NewPointersGoDirectlyToViz() {
-  using base::android::android_info::SdkVersion;
-  if (base::android::android_info::sdk_int() ==
-      base::android::android_info::SDK_VERSION_V) {
-    // Some of the earlier Android 15 versions might have new pointer downs
-    // going to Viz, but we are defensively saying `false` here since there's no
-    // reliable way to check this which builds of Android 15 have new pointers
-    // going to Viz.
-    return false;
-  }
-
-  // The fix referred to in comments below: http://ag/32215438.
-  if (base::android::android_info::sdk_int() ==
-      base::android::android_info::SDK_VERSION_BAKLAVA) {
-    if (base::android::android_info::manufacturer() == "Google") {
-      // All Google's Android 16 devices have the fix available and pointer
-      // downs go directly to Viz.
-      return true;
-    } else {
-      // Some of OEMs might not have picked up the fix which was done quite late
-      // in the release cycle of Android 16.
-      return false;
-    }
-  }
-
-  CHECK_GT(base::android::android_info::sdk_int(),
-           base::android::android_info::SDK_VERSION_BAKLAVA);
-  // On Android 17+, the fix is always available, the pointer downs from the
-  // same touch sequence will go to Viz.
-  return true;
-}
-
 void InputTransferHandlerAndroid::OnStartDroppingSequence(
     const ui::MotionEventAndroid& event,
     InputOnVizSequenceDroppedReason reason) {
diff --git a/content/browser/renderer_host/input/input_transfer_handler_android.h b/content/browser/renderer_host/input/input_transfer_handler_android.h
index 0b8b0f9c..34181e5 100644
--- a/content/browser/renderer_host/input/input_transfer_handler_android.h
+++ b/content/browser/renderer_host/input/input_transfer_handler_android.h
@@ -82,8 +82,6 @@
 
   void OnTouchEnd(base::TimeTicks event_time);
 
-  static bool NewPointersGoDirectlyToViz();
-
   // Virtual for testing.
   virtual bool IsTouchSequencePotentiallyActiveOnViz() const;
 
diff --git a/content/browser/renderer_host/input/input_transfer_handler_android_unittest.cc b/content/browser/renderer_host/input/input_transfer_handler_android_unittest.cc
index 90d3f313..7fb3b68 100644
--- a/content/browser/renderer_host/input/input_transfer_handler_android_unittest.cc
+++ b/content/browser/renderer_host/input/input_transfer_handler_android_unittest.cc
@@ -346,8 +346,7 @@
     bool should_retransfer =
         std::find(browser_handling_cases.begin(), browser_handling_cases.end(),
                   static_cast<TransferInputToVizResult>(transfer_result)) !=
-            browser_handling_cases.end() &&
-        !InputTransferHandlerAndroid::NewPointersGoDirectlyToViz();
+        browser_handling_cases.end();
     EXPECT_CALL(*mock_, MaybeTransferInputToViz(_))
         .WillOnce(Return(static_cast<int>(transfer_result)));
     if (should_retransfer) {
@@ -360,16 +359,8 @@
           .Times(1);
     }
 
-    // In case the transfer was successful, the events are consumed until next
-    // cancel.
-    const bool expected_sequence_dropped =
-        !InputTransferHandlerAndroid::NewPointersGoDirectlyToViz() ||
-        static_cast<TransferInputToVizResult>(transfer_result) ==
-            TransferInputToVizResult::kSuccessfullyTransferred;
-    EXPECT_EQ(transfer_handler_->OnTouchEvent(*down_event_2),
-              expected_sequence_dropped);
-    EXPECT_EQ(transfer_handler_->OnTouchEvent(*cancel_event_2),
-              expected_sequence_dropped);
+    EXPECT_TRUE(transfer_handler_->OnTouchEvent(*down_event_2));
+    EXPECT_TRUE(transfer_handler_->OnTouchEvent(*cancel_event_2));
 
     event_time += base::Milliseconds(20);
     transfer_handler_->OnTouchEnd(event_time);
@@ -412,31 +403,23 @@
   // We haven't received a notification from Viz for TouchEnd yet.
   // The new sequence should be dropped, instead of Browser and Viz
   // potentially handling the seequence at same time.
-  const bool expected_sequence_dropped =
-      !InputTransferHandlerAndroid::NewPointersGoDirectlyToViz();
-
   base::HistogramTester histogram_tester;
-  EXPECT_EQ(transfer_handler_->OnTouchEvent(*down_event_2),
-            expected_sequence_dropped);
-  EXPECT_EQ(transfer_handler_->OnTouchEvent(*move_event_2),
-            expected_sequence_dropped);
-  EXPECT_EQ(transfer_handler_->OnTouchEvent(*cancel_event_2),
-            expected_sequence_dropped);
-  if (expected_sequence_dropped) {
-    const int num_dropped_events = 3;
-    histogram_tester.ExpectUniqueSample(
-        InputTransferHandlerAndroid::kEventsInDroppedSequenceHistogram,
-        num_dropped_events, 1);
-    histogram_tester.ExpectBucketCount(
-        InputTransferHandlerAndroid::kEventTypesInDroppedSequenceHistogram,
-        ui::MotionEvent::Action::DOWN, 1);
-    histogram_tester.ExpectBucketCount(
-        InputTransferHandlerAndroid::kEventTypesInDroppedSequenceHistogram,
-        ui::MotionEvent::Action::MOVE, 1);
-    histogram_tester.ExpectBucketCount(
-        InputTransferHandlerAndroid::kEventTypesInDroppedSequenceHistogram,
-        ui::MotionEvent::Action::CANCEL, 1);
-  }
+  EXPECT_TRUE(transfer_handler_->OnTouchEvent(*down_event_2));
+  EXPECT_TRUE(transfer_handler_->OnTouchEvent(*move_event_2));
+  EXPECT_TRUE(transfer_handler_->OnTouchEvent(*cancel_event_2));
+  const int num_dropped_events = 3;
+  histogram_tester.ExpectUniqueSample(
+      InputTransferHandlerAndroid::kEventsInDroppedSequenceHistogram,
+      num_dropped_events, 1);
+  histogram_tester.ExpectBucketCount(
+      InputTransferHandlerAndroid::kEventTypesInDroppedSequenceHistogram,
+      ui::MotionEvent::Action::DOWN, 1);
+  histogram_tester.ExpectBucketCount(
+      InputTransferHandlerAndroid::kEventTypesInDroppedSequenceHistogram,
+      ui::MotionEvent::Action::MOVE, 1);
+  histogram_tester.ExpectBucketCount(
+      InputTransferHandlerAndroid::kEventTypesInDroppedSequenceHistogram,
+      ui::MotionEvent::Action::CANCEL, 1);
 }
 
 TEST_F(InputTransferHandlerTest,
@@ -588,13 +571,7 @@
   down_time = event_time + base::Milliseconds(8);
   auto down_event_2 = GetMotionEventAndroid(
       ui::MotionEvent::Action::DOWN, event_time, down_time, finger_pointer_);
-
-  if (InputTransferHandlerAndroid::NewPointersGoDirectlyToViz()) {
-    // The touch sequence can be handled by Browser.
-    EXPECT_FALSE(transfer_handler_->OnTouchEvent(*down_event_2));
-  } else {
-    EXPECT_TRUE(transfer_handler_->OnTouchEvent(*down_event_2));
-  }
+  EXPECT_TRUE(transfer_handler_->OnTouchEvent(*down_event_2));
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
index c07b61c..d3ae3ff 100644
--- a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
+++ b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
@@ -122,7 +122,8 @@
   kScreenCaptureKitDeviceMac = 2,
   kDesktopCaptureDeviceMac = 3,
   kLegacyDesktopCaptureDevice = 4,
-  kImplementationCount = 5,
+  kNativeMacOSPickerCaptureDevice = 5,
+  kImplementationCount = 6,
 };
 
 // These values are persisted to logs. Entries should not be renumbered and
@@ -148,7 +149,11 @@
   kLegacyDesktopCaptureDeviceTypeScreen = 17,
   kLegacyDesktopCaptureDeviceTypeWindow = 18,
   kLegacyDesktopCaptureDeviceTypeWebContents = 19,
-  kMaxValue = kLegacyDesktopCaptureDeviceTypeWebContents,
+  kNativeMacOSPickerCaptureDeviceTypeNone = 20,
+  kNativeMacOSPickerCaptureDeviceTypeScreen = 21,
+  kNativeMacOSPickerCaptureDeviceTypeWindow = 22,
+  kNativeMacOSPickerCaptureDeviceTypeWebContents = 23,
+  kMaxValue = kNativeMacOSPickerCaptureDeviceTypeWebContents,
 };
 
 void ReportDesktopCaptureImplementationAndType(
@@ -177,7 +182,7 @@
   if (picker) {
     device_out = picker->CreateDevice(desktop_id);
     if (device_out) {
-      return kScreenCaptureKitDeviceMac;
+      return kNativeMacOSPickerCaptureDevice;
     }
     return kNoImplementation;
   }
diff --git a/content/browser/renderer_host/media/preferred_audio_output_device_manager.h b/content/browser/renderer_host/media/preferred_audio_output_device_manager.h
index 54a752e..1221e8c4 100644
--- a/content/browser/renderer_host/media/preferred_audio_output_device_manager.h
+++ b/content/browser/renderer_host/media/preferred_audio_output_device_manager.h
@@ -14,7 +14,6 @@
 #include "content/browser/renderer_host/media/audio_output_authorization_handler.h"
 #include "content/public/browser/global_routing_id.h"
 #include "media/base/output_device_info.h"
-#include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/renderer_host/navigation_transitions/navigation_entry_screenshot.cc b/content/browser/renderer_host/navigation_transitions/navigation_entry_screenshot.cc
index 961dbba..1ed58592 100644
--- a/content/browser/renderer_host/navigation_transitions/navigation_entry_screenshot.cc
+++ b/content/browser/renderer_host/navigation_transitions/navigation_entry_screenshot.cc
@@ -155,6 +155,7 @@
 NavigationEntryScreenshot::~NavigationEntryScreenshot() {
   if (cache_) {
     cache_->OnNavigationEntryGone(unique_id_);
+    cache_ = nullptr;
   }
   if (read_back_needed_ || compression_task_) {
     auto observer_list =
diff --git a/content/browser/renderer_host/render_frame_host_android.cc b/content/browser/renderer_host/render_frame_host_android.cc
index 1550be0..54a524a8 100644
--- a/content/browser/renderer_host/render_frame_host_android.cc
+++ b/content/browser/renderer_host/render_frame_host_android.cc
@@ -275,6 +275,26 @@
           base::android::ScopedJavaGlobalRef<jobject>(callback)));
 }
 
+void RenderFrameHostAndroid::PerformReportWebAuthSecurityChecks(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& relying_party_id,
+    const base::android::JavaParamRef<jobject>& effective_origin,
+    const base::android::JavaParamRef<jobject>& callback) const {
+  url::Origin origin = url::Origin::FromJavaObject(env, effective_origin);
+  render_frame_host_->PerformReportWebAuthSecurityChecks(
+      ConvertJavaStringToUTF8(env, relying_party_id), origin,
+      base::BindOnce(
+          [](base::android::ScopedJavaGlobalRef<jobject> callback,
+             blink::mojom::AuthenticatorStatus status, bool is_cross_origin) {
+            base::android::RunObjectCallbackAndroid(
+                callback,
+                Java_RenderFrameHostImpl_createWebAuthSecurityChecksResults(
+                    base::android::AttachCurrentThread(),
+                    static_cast<jint>(status), is_cross_origin));
+          },
+          base::android::ScopedJavaGlobalRef<jobject>(callback)));
+}
+
 jint RenderFrameHostAndroid::GetLifecycleState(JNIEnv* env) const {
   return static_cast<jint>(render_frame_host_->GetLifecycleState());
 }
diff --git a/content/browser/renderer_host/render_frame_host_android.h b/content/browser/renderer_host/render_frame_host_android.h
index 12d3eaa6..747e863 100644
--- a/content/browser/renderer_host/render_frame_host_android.h
+++ b/content/browser/renderer_host/render_frame_host_android.h
@@ -92,6 +92,12 @@
           remote_desktop_client_override_origin,
       const base::android::JavaParamRef<jobject>& callback) const;
 
+  void PerformReportWebAuthSecurityChecks(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jstring>&,
+      const base::android::JavaParamRef<jobject>&,
+      const base::android::JavaParamRef<jobject>& callback) const;
+
   jint GetLifecycleState(JNIEnv* env) const;
 
   void InsertVisualStateCallback(
diff --git a/content/browser/renderer_host/render_frame_host_delegate.cc b/content/browser/renderer_host/render_frame_host_delegate.cc
index 06737ac..a2ee5356 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.cc
+++ b/content/browser/renderer_host/render_frame_host_delegate.cc
@@ -34,12 +34,6 @@
                                                    site_for_cookies);
 }
 
-bool RenderFrameHostDelegate::OnMessageReceived(
-    RenderFrameHostImpl* render_frame_host,
-    const IPC::Message& message) {
-  return false;
-}
-
 bool RenderFrameHostDelegate::DidAddMessageToConsole(
     RenderFrameHostImpl* source_frame,
     blink::mojom::ConsoleMessageLevel log_level,
diff --git a/content/browser/renderer_host/render_frame_host_delegate.h b/content/browser/renderer_host/render_frame_host_delegate.h
index 6d5b2a6..4342739 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.h
+++ b/content/browser/renderer_host/render_frame_host_delegate.h
@@ -68,10 +68,6 @@
 
 class GURL;
 
-namespace IPC {
-class Message;
-}
-
 namespace gfx {
 class Rect;
 class Size;
@@ -164,10 +160,6 @@
   using ClipboardEndpoint = content::ClipboardEndpoint;
   using ClipboardMetadata = ui::ClipboardMetadata;
 
-  // This is used to give the delegate a chance to filter IPC messages.
-  virtual bool OnMessageReceived(RenderFrameHostImpl* render_frame_host,
-                                 const IPC::Message& message);
-
   // Notification from the renderer host that a suspicious navigation of the
   // main frame has been blocked. Allows the delegate to provide some UI to let
   // the user know about the blocked navigation and give them the option to
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index d694d115..6141bd0 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -3767,21 +3767,6 @@
                                             : PageVisibilityState::kVisible;
 }
 
-bool RenderFrameHostImpl::OnMessageReceived(const IPC::Message& msg) {
-  // Only process messages if the RenderFrame is alive.
-  if (!is_render_frame_created())
-    return false;
-
-  // Crash reports triggered by IPC messages for this frame should be associated
-  // with its URL.
-  ScopedActiveURL scoped_active_url(this);
-
-  if (delegate_->OnMessageReceived(this, msg))
-    return true;
-
-  return false;
-}
-
 void RenderFrameHostImpl::OnAssociatedInterfaceRequest(
     const std::string& interface_name,
     mojo::ScopedInterfaceEndpointHandle handle) {
@@ -17990,10 +17975,10 @@
           GetWebAuthRequestSecurityChecker()->ValidateDomainAndRelyingPartyID(
               effective_origin, relying_party_id, request_type,
               remote_desktop_client_override_origin,
-              base::BindOnce(&RenderFrameHostImpl::
-                                 OnGetAssertionWebAuthSecurityChecksCompleted,
-                             weak_ptr_factory_.GetWeakPtr(),
-                             std::move(callback), is_cross_origin));
+              base::BindOnce(
+                  &RenderFrameHostImpl::OnWebAuthSecurityChecksCompleted,
+                  weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                  is_cross_origin));
 
   // If `remote_validation` is nullptr then this object may already have been
   // destroyed.
@@ -18002,14 +17987,6 @@
   }
 }
 
-void RenderFrameHostImpl::OnGetAssertionWebAuthSecurityChecksCompleted(
-    base::OnceCallback<void(blink::mojom::AuthenticatorStatus, bool)> callback,
-    bool is_cross_origin,
-    blink::mojom::AuthenticatorStatus status) {
-  webauthn_remote_rp_id_validation_.reset();
-  std::move(callback).Run(status, is_cross_origin);
-}
-
 void RenderFrameHostImpl::PerformMakeCredentialWebAuthSecurityChecks(
     const std::string& relying_party_id,
     const url::Origin& effective_origin,
@@ -18043,10 +18020,10 @@
           GetWebAuthRequestSecurityChecker()->ValidateDomainAndRelyingPartyID(
               effective_origin, relying_party_id, request_type,
               remote_desktop_client_override_origin,
-              base::BindOnce(&RenderFrameHostImpl::
-                                 OnMakeCredentialWebAuthSecurityChecksCompleted,
-                             weak_ptr_factory_.GetWeakPtr(),
-                             std::move(callback), is_cross_origin));
+              base::BindOnce(
+                  &RenderFrameHostImpl::OnWebAuthSecurityChecksCompleted,
+                  weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                  is_cross_origin));
 
   // If `remote_validation` is nullptr then this object may already have been
   // destroyed.
@@ -18055,7 +18032,48 @@
   }
 }
 
-void RenderFrameHostImpl::OnMakeCredentialWebAuthSecurityChecksCompleted(
+void RenderFrameHostImpl::PerformReportWebAuthSecurityChecks(
+    const std::string& relying_party_id,
+    const url::Origin& effective_origin,
+    base::OnceCallback<void(blink::mojom::AuthenticatorStatus, bool)>
+        callback) {
+  bool is_cross_origin = true;
+
+  blink::mojom::AuthenticatorStatus status =
+      GetWebAuthRequestSecurityChecker()->ValidateAncestorOrigins(
+          effective_origin, WebAuthRequestSecurityChecker::RequestType::kReport,
+          &is_cross_origin);
+  if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
+    std::move(callback).Run(status, is_cross_origin);
+    return;
+  }
+
+  if (!GetContentClient()->browser()->IsSecurityLevelAcceptableForWebAuthn(
+          this, effective_origin)) {
+    std::move(callback).Run(
+        blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR, is_cross_origin);
+    return;
+  }
+
+  std::unique_ptr<WebAuthRequestSecurityChecker::RemoteValidation>
+      remote_validation =
+          GetWebAuthRequestSecurityChecker()->ValidateDomainAndRelyingPartyID(
+              effective_origin, relying_party_id,
+              WebAuthRequestSecurityChecker::RequestType::kReport,
+              /*remote_desktop_client_override_origin=*/std::nullopt,
+              base::BindOnce(
+                  &RenderFrameHostImpl::OnWebAuthSecurityChecksCompleted,
+                  weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                  is_cross_origin));
+
+  // If `remote_validation` is nullptr then this object may already have been
+  // destroyed.
+  if (remote_validation) {
+    webauthn_remote_rp_id_validation_ = std::move(remote_validation);
+  }
+}
+
+void RenderFrameHostImpl::OnWebAuthSecurityChecksCompleted(
     base::OnceCallback<void(blink::mojom::AuthenticatorStatus, bool)> callback,
     bool is_cross_origin,
     blink::mojom::AuthenticatorStatus status) {
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index f72011c..e5fb8916 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -723,7 +723,6 @@
   blink::web_pref::WebPreferences GetOrCreateWebPreferences();
 
   // IPC::Listener
-  bool OnMessageReceived(const IPC::Message& msg) override;
   void OnAssociatedInterfaceRequest(
       const std::string& interface_name,
       mojo::ScopedInterfaceEndpointHandle handle) override;
@@ -3031,6 +3030,11 @@
       const std::optional<url::Origin>& remote_desktop_client_override_origin,
       base::OnceCallback<void(blink::mojom::AuthenticatorStatus, bool)>
           callback);
+  void PerformReportWebAuthSecurityChecks(
+      const std::string& relying_party_id,
+      const url::Origin& effective_origin,
+      base::OnceCallback<void(blink::mojom::AuthenticatorStatus, bool)>
+          callback);
 #endif
 
   using JavaScriptResultAndTypeCallback =
@@ -4405,15 +4409,9 @@
   std::optional<mojo::UrgentMessageScope> MakeUrgentMessageScopeIfNeeded();
 
 #if BUILDFLAG(IS_ANDROID)
-  // These functions are called after a WebAuthn relying party check has
-  // completed. See `PerformMakeCredentialWebAuthSecurityChecks` and
-  // `PerformGetAssertionWebAuthSecurityChecks`.
-  void OnGetAssertionWebAuthSecurityChecksCompleted(
-      base::OnceCallback<void(blink::mojom::AuthenticatorStatus, bool)>
-          callback,
-      bool is_cross_origin,
-      blink::mojom::AuthenticatorStatus status);
-  void OnMakeCredentialWebAuthSecurityChecksCompleted(
+  // This function is called after a WebAuthn relying party check has
+  // completed. See `Perform*WebAuthSecurityChecks`.
+  void OnWebAuthSecurityChecksCompleted(
       base::OnceCallback<void(blink::mojom::AuthenticatorStatus, bool)>
           callback,
       bool is_cross_origin,
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 1bc649d..3b6a7dcd 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1646,9 +1646,9 @@
   const int id = GetDeprecatedID();
   const uint64_t tracing_id =
       ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(id);
-  gpu_client_.reset(
-      new viz::GpuClient(std::make_unique<BrowserGpuClientDelegate>(), id,
-                         tracing_id, GetUIThreadTaskRunner({})));
+  gpu_client_ = std::make_unique<viz::GpuClient>(
+      std::make_unique<BrowserGpuClientDelegate>(), id, tracing_id,
+      /*enable_extra_handles_validation=*/false, GetUIThreadTaskRunner({}));
 }
 
 // static
@@ -3957,44 +3957,6 @@
   return true;
 }
 
-bool RenderProcessHostImpl::Send(IPC::Message* msg) {
-  TRACE_IPC_MESSAGE_SEND("renderer_host", "RenderProcessHostImpl::Send", msg);
-
-  std::unique_ptr<IPC::Message> message(msg);
-
-  // |channel_| is only null after Cleanup(), at which point we don't care
-  // about delivering any messages.
-  if (!channel_)
-    return false;
-
-  DCHECK(!message->is_sync());
-  return channel_->Send(message.release());
-}
-
-bool RenderProcessHostImpl::OnMessageReceived(const IPC::Message& msg) {
-  // If we're about to be deleted, or have initiated the fast shutdown
-  // sequence, we ignore incoming messages.
-
-  if (deleting_soon_ || fast_shutdown_started_)
-    return false;
-
-  mark_child_process_activity_time();
-
-  // Dispatch incoming messages to the appropriate IPC::Listener.
-  IPC::Listener* listener = listeners_.Lookup(msg.routing_id());
-  if (!listener) {
-    if (msg.is_sync()) {
-      // The listener has gone away, so we must respond or else the caller
-      // will hang waiting for a reply.
-      IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg);
-      reply->set_reply_error();
-      Send(reply);
-    }
-    return true;
-  }
-  return listener->OnMessageReceived(msg);
-}
-
 void RenderProcessHostImpl::OnAssociatedInterfaceRequest(
     const std::string& interface_name,
     mojo::ScopedInterfaceEndpointHandle handle) {
@@ -4052,17 +4014,10 @@
   ProcessDied(info);
 }
 
-void RenderProcessHostImpl::OnBadMessageReceived(const IPC::Message& message) {
+void RenderProcessHostImpl::OnBadMessageReceived() {
   // Message de-serialization failed. We consider this a capital crime. Kill
   // the renderer if we have one.
-  auto type = message.type();
-  LOG(ERROR) << "bad message " << type << " terminating renderer.";
-
-  // The ReceivedBadMessage call below will trigger a DumpWithoutCrashing.
-  // Alias enough information here so that we can determine what the bad
-  // message was.
-  base::debug::Alias(&type);
-
+  LOG(ERROR) << "bad message, terminating renderer.";
   bad_message::ReceivedBadMessage(this,
                                   bad_message::RPH_DESERIALIZATION_FAILED);
 }
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 0da7d80..21b1685 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -371,17 +371,13 @@
   void ResumeSocketManagerForRenderFrameHost(
       const GlobalRenderFrameHostId& render_frame_host_id) override;
 
-  // IPC::Sender via RenderProcessHost.
-  bool Send(IPC::Message* msg) override;
-
   // IPC::Listener via RenderProcessHost.
-  bool OnMessageReceived(const IPC::Message& msg) override;
   void OnAssociatedInterfaceRequest(
       const std::string& interface_name,
       mojo::ScopedInterfaceEndpointHandle handle) override;
   void OnChannelConnected(int32_t peer_pid) override;
   void OnChannelError() override;
-  void OnBadMessageReceived(const IPC::Message& message) override;
+  void OnBadMessageReceived() override;
 
   // ChildProcessLauncher::Client implementation.
   void OnProcessLaunched() override;
diff --git a/content/browser/service_host/service_process_host_impl.cc b/content/browser/service_host/service_process_host_impl.cc
index d9c14f9..c723fee 100644
--- a/content/browser/service_host/service_process_host_impl.cc
+++ b/content/browser/service_host/service_process_host_impl.cc
@@ -66,7 +66,8 @@
 #endif  // BUILDFLAG(IS_WIN)
   if (service_options.allow_gpu_client.has_value() &&
       service_options.allow_gpu_client.value()) {
-    utility_options.WithGpuClientAllowed();
+    utility_options.WithGpuClientAllowed(
+        service_options.extra_handles_validation.value_or(false));
   }
 
   utility_options.WithBoundServiceInterfaceOnChildProcess(std::move(receiver));
diff --git a/content/browser/service_host/utility_process_host.cc b/content/browser/service_host/utility_process_host.cc
index b4d84672..e98063d 100644
--- a/content/browser/service_host/utility_process_host.cc
+++ b/content/browser/service_host/utility_process_host.cc
@@ -82,7 +82,8 @@
 #include "services/network/public/mojom/network_service.mojom.h"
 #endif
 
-#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
+#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) || \
+    BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
 #include "base/task/sequenced_task_runner.h"
 #include "components/viz/host/gpu_client.h"
 #include "media/capture/capture_switches.h"
@@ -156,7 +157,8 @@
 #else
       child_flags_(ChildProcessHost::CHILD_NORMAL),
 #endif
-#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
+#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) || \
+    BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
       allowed_gpu_(false),
 #endif  // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
       file_data_(std::make_unique<ChildProcessLauncherFileData>()),
@@ -172,7 +174,8 @@
 UtilityProcessHost::UtilityProcessHost(Options options,
                                        std::unique_ptr<Client> client)
     : options_(std::move(options)),
-#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
+#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) || \
+    BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
       gpu_client_(nullptr, base::OnTaskRunnerDeleter(nullptr)),
 #endif  // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
       client_(std::move(client)) {
@@ -227,10 +230,12 @@
 }
 #endif  // BUILDFLAG(IS_WIN)
 
-UtilityProcessHost::Options&
-UtilityProcessHost::Options::WithGpuClientAllowed() {
-#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
+UtilityProcessHost::Options& UtilityProcessHost::Options::WithGpuClientAllowed(
+    bool extra_handles_validation) {
+#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) || \
+    BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
   allowed_gpu_ = true;
+  extra_handles_validation_ = extra_handles_validation;
 #endif  // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
   return *this;
 }
diff --git a/content/browser/service_host/utility_process_host.h b/content/browser/service_host/utility_process_host.h
index 580fa66..910e8a4 100644
--- a/content/browser/service_host/utility_process_host.h
+++ b/content/browser/service_host/utility_process_host.h
@@ -37,7 +37,8 @@
 class Thread;
 }  // namespace base
 
-#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
+#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) || \
+    BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
 namespace viz {
 class GpuClient;
 }  // namespace viz
@@ -124,7 +125,7 @@
 #endif  // BUILDFLAG(IS_WIN)
 
     // Allows the child process to bind viz.mojom.Gpu.
-    Options& WithGpuClientAllowed();
+    Options& WithGpuClientAllowed(bool extra_handles_validation);
 
 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC)
     // Adds to ChildProcessLauncherFileData::files_to_preload, which maps |key|
@@ -188,6 +189,9 @@
 #if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
     // Whether or not to bind viz::mojom::Gpu to the utility process.
     bool allowed_gpu_;
+    // Whether or not the gpu channel will perform extra validation on handles
+    // sent by the utility process.
+    bool extra_handles_validation_ = false;
 #endif  // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
 
     // A mojo receiver to bind once the process starts.
@@ -247,7 +251,8 @@
   };
   LaunchState launch_state_ = LaunchState::kLaunchInProgress;
 
-#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
+#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) || \
+    BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
   std::unique_ptr<viz::GpuClient, base::OnTaskRunnerDeleter> gpu_client_;
 #endif  // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
 
diff --git a/content/browser/service_host/utility_process_host_receiver_bindings.cc b/content/browser/service_host/utility_process_host_receiver_bindings.cc
index 9b1f9e4..aa1dcb7 100644
--- a/content/browser/service_host/utility_process_host_receiver_bindings.cc
+++ b/content/browser/service_host/utility_process_host_receiver_bindings.cc
@@ -15,7 +15,8 @@
 #include "content/browser/font_service.h"  // nogncheck
 #endif
 
-#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
+#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) || \
+    BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
 #include "components/viz/host/gpu_client.h"
 #include "content/public/browser/gpu_client.h"
 #endif  // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
@@ -30,11 +31,13 @@
     return;
   }
 #endif
-#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
+#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) || \
+    BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
   if (options_.allowed_gpu_) {
     // TODO(crbug.com/328099369) Remove once all clients get this directly.
     if (auto gpu_receiver = receiver.As<viz::mojom::Gpu>()) {
-      gpu_client_ = content::CreateGpuClient(std::move(gpu_receiver));
+      gpu_client_ = content::CreateGpuClient(
+          std::move(gpu_receiver), options_.extra_handles_validation_);
       return;
     }
   }
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.h b/content/browser/service_worker/service_worker_controllee_request_handler.h
index d1c461e0..eaecf4c 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.h
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.h
@@ -19,8 +19,6 @@
 #include "content/common/content_export.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/single_request_url_loader_factory.h"
-#include "services/network/public/mojom/fetch_api.mojom.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/service_worker/service_worker_main_resource_handle.h b/content/browser/service_worker/service_worker_main_resource_handle.h
index e89617a6..0ce94e19 100644
--- a/content/browser/service_worker/service_worker_main_resource_handle.h
+++ b/content/browser/service_worker/service_worker_main_resource_handle.h
@@ -8,8 +8,11 @@
 #include "base/memory/weak_ptr.h"
 #include "content/browser/service_worker/service_worker_accessed_callback.h"
 #include "content/common/content_export.h"
-#include "services/network/public/mojom/network_context.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
+#include "net/base/isolation_info.h"
+
+namespace network {
+struct ResourceRequest;
+}
 
 namespace content {
 
diff --git a/content/browser/sms/user_consent_handler.h b/content/browser/sms/user_consent_handler.h
index 874f8c8..f90a92840 100644
--- a/content/browser/sms/user_consent_handler.h
+++ b/content/browser/sms/user_consent_handler.h
@@ -5,10 +5,9 @@
 #ifndef CONTENT_BROWSER_SMS_USER_CONSENT_HANDLER_H_
 #define CONTENT_BROWSER_SMS_USER_CONSENT_HANDLER_H_
 
-#include "base/functional/callback_forward.h"
+#include "base/functional/callback.h"
 #include "base/memory/raw_ref.h"
 #include "content/common/content_export.h"
-#include "third_party/blink/public/mojom/sms/webotp_service.mojom-shared.h"
 #include "url/origin.h"
 
 namespace content {
diff --git a/content/browser/speech/speech_recognizer.h b/content/browser/speech/speech_recognizer.h
index c3d432d..8b399c0 100644
--- a/content/browser/speech/speech_recognizer.h
+++ b/content/browser/speech/speech_recognizer.h
@@ -9,7 +9,10 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "content/common/content_export.h"
-#include "media/mojo/mojom/speech_recognition_recognition_context.h"
+
+namespace media {
+struct SpeechRecognitionRecognitionContext;
+}
 
 namespace content {
 
diff --git a/content/browser/video_capture_service_impl.cc b/content/browser/video_capture_service_impl.cc
index 8d14685..0267fe7 100644
--- a/content/browser/video_capture_service_impl.cc
+++ b/content/browser/video_capture_service_impl.cc
@@ -47,7 +47,8 @@
     ServiceProcessHost::Options options;
     options.WithDisplayName("Video Capture");
     // TODO(crbug.com/328099369) Remove once gpu client is provided directly.
-    options.WithGpuClient(ServiceProcessHostGpuClient::GetPassKey());
+    options.WithGpuClient(/*enable_extra_handles_validation=*/false,
+                          ServiceProcessHostGpuClient::GetPassKey());
 #if BUILDFLAG(IS_MAC)
     // On Mac, the service requires a CFRunLoop which is provided by a
     // UI message loop. See https://crbug.com/834581.
diff --git a/content/browser/video_capture_service_impl.h b/content/browser/video_capture_service_impl.h
index cd034bb..5c039b6 100644
--- a/content/browser/video_capture_service_impl.h
+++ b/content/browser/video_capture_service_impl.h
@@ -6,7 +6,6 @@
 #define CONTENT_BROWSER_VIDEO_CAPTURE_SERVICE_IMPL_H_
 
 #include "content/common/content_export.h"
-#include "services/video_capture/public/mojom/video_capture_service.mojom-forward.h"
 
 namespace content {
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index d7f46a1..b484a95 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1724,20 +1724,6 @@
       ->node_.GetInnerWebContentsInFrame(frame_tree_node);
 }
 
-bool WebContentsImpl::OnMessageReceived(RenderFrameHostImpl* render_frame_host,
-                                        const IPC::Message& message) {
-  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnMessageReceived",
-                        "render_frame_host", render_frame_host);
-
-  for (auto& observer : observers_.observer_list()) {
-    if (observer.OnMessageReceived(message, render_frame_host)) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
 std::string WebContentsImpl::GetTitleForMediaControls() {
   OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::GetTitleForMediaControls");
 
@@ -3297,6 +3283,11 @@
       inner_render_manager->current_frame_host();
   RenderViewHostImpl* inner_render_view_host =
       inner_main_frame->render_view_host();
+  RenderFrameHostImpl* inner_speculative_frame =
+      inner_render_manager->speculative_frame_host();
+  RenderViewHostImpl* inner_speculative_render_view_host =
+      inner_speculative_frame ? inner_speculative_frame->render_view_host()
+                              : nullptr;
   auto* outer_render_manager =
       render_frame_host_impl->frame_tree_node()->render_manager();
 
@@ -3321,6 +3312,16 @@
       prev_rwhv->Destroy();
     }
   }
+  // Do the same for speculative render frame host's view.
+  if (inner_speculative_frame) {
+    RenderWidgetHostViewBase* prev_speculative_rwhv =
+        static_cast<RenderWidgetHostViewBase*>(
+            inner_speculative_frame->GetView());
+    if (prev_speculative_rwhv &&
+        !prev_speculative_rwhv->IsRenderWidgetHostViewChildFrame()) {
+      prev_speculative_rwhv->Destroy();
+    }
+  }
 
   // When the WebContents being initialized has not already navigated, the
   // browser side Render{View,Frame}Host must be initialized and the
@@ -3335,6 +3336,20 @@
     inner_web_contents_impl->CreateRenderWidgetHostViewForRenderManager(
         inner_render_view_host);
   }
+  // Do the same for speculative render frame host.
+  if (inner_speculative_render_view_host) {
+    inner_render_manager->InitRenderView(
+        inner_speculative_frame->GetSiteInstance()->group(),
+        inner_speculative_render_view_host,
+        /*proxy=*/nullptr, /*navigation_metrics_token=*/std::nullopt);
+    RenderWidgetHostViewBase* speculative_rwhv =
+        static_cast<RenderWidgetHostViewBase*>(
+            inner_speculative_frame->GetView());
+    if (!speculative_rwhv) {
+      inner_web_contents_impl->CreateRenderWidgetHostViewForRenderManager(
+          inner_speculative_render_view_host);
+    }
+  }
 
   inner_web_contents_impl->RecursivelyUnregisterRenderWidgetHostViews();
 
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 342a5f9..0a739681 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -678,8 +678,6 @@
   void SetOverscrollNavigationEnabled(bool enabled) override;
 
   // RenderFrameHostDelegate ---------------------------------------------------
-  bool OnMessageReceived(RenderFrameHostImpl* render_frame_host,
-                         const IPC::Message& message) override;
   void OnDidBlockNavigation(
       const GURL& blocked_url,
       const GURL& initiator_url,
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 825add16..ea6d087 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -5132,6 +5132,102 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(
+    UnownedInnerWebContentsBrowserTest,
+    AttachUnownedInnerWebContentsWithPendingCrossSiteNavigation) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  const GURL outer_url(
+      embedded_test_server()->GetURL("a.com", "/page_with_iframe.html"));
+  const GURL inner_url_a(
+      embedded_test_server()->GetURL("a.com", "/title1.html"));
+  const GURL inner_url_b(
+      embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  // Setup outer WebContents.
+  ASSERT_TRUE(NavigateToURL(shell(), outer_url));
+  WebContentsImpl* outer_wc =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  RenderFrameHostImpl* iframe_rfh = static_cast<RenderFrameHostImpl*>(
+      ChildFrameAt(outer_wc->GetPrimaryMainFrame(), 0));
+  ASSERT_TRUE(iframe_rfh);
+
+  // Setup inner WebContents with a pending cross site navigation.
+  WebContents::CreateParams inner_params(
+      shell()->web_contents()->GetBrowserContext());
+  std::unique_ptr<WebContents> inner_wc = WebContents::Create(inner_params);
+  WebContentsImpl* inner_wc_impl =
+      static_cast<WebContentsImpl*>(inner_wc.get());
+  ASSERT_TRUE(NavigateToURL(inner_wc.get(), inner_url_a));
+  // Navigate inner WebContents and pause after speculative render frame host is
+  // is created for the cross site navigation at which time the view for it is
+  // also created.
+  TestNavigationManager nav_manager_b(inner_wc_impl, inner_url_b);
+  inner_wc->GetController().LoadURLWithParams(
+      NavigationController::LoadURLParams(inner_url_b));
+  nav_manager_b.WaitForSpeculativeRenderFrameHostCreation();
+
+  // Get the RenderFrameHosts in inner WebContents.
+  RenderFrameHost* rfh_a = inner_wc->GetPrimaryMainFrame();
+  ASSERT_TRUE(rfh_a);
+  // Cross site pending navigation should be using speculative render frame host
+  // for the navigation.
+  RenderFrameHost* rfh_b = inner_wc_impl->GetPrimaryFrameTree()
+                               .root()
+                               ->render_manager()
+                               ->speculative_frame_host();
+  ASSERT_TRUE(rfh_b);
+
+  // Verify that RenderWidgetHostViews have not changed to
+  // RenderWidgetHostViewChildFrame yet.
+  {
+    auto* rwhv_a = static_cast<RenderWidgetHostViewBase*>(rfh_a->GetView());
+    ASSERT_TRUE(rwhv_a);
+    ASSERT_FALSE(rwhv_a->IsRenderWidgetHostViewChildFrame());
+    auto* rwhv_b = static_cast<RenderWidgetHostViewBase*>(rfh_b->GetView());
+    ASSERT_TRUE(rwhv_b);
+    ASSERT_FALSE(rwhv_b->IsRenderWidgetHostViewChildFrame());
+  }
+
+  // Attach the inner WebContents.
+  outer_wc->AttachUnownedInnerWebContents(
+      UnownedInnerWebContentsClient::GetPassKeyForTesting(), inner_wc.get(),
+      iframe_rfh);
+  ASSERT_EQ(outer_wc, inner_wc->GetOuterWebContents());
+
+  // Verify that RenderFrameHosts are not changed after attaching inner
+  // WebContents.
+  EXPECT_EQ(rfh_a, inner_wc->GetPrimaryMainFrame());
+  EXPECT_EQ(rfh_b, inner_wc_impl->GetPrimaryFrameTree()
+                       .root()
+                       ->render_manager()
+                       ->speculative_frame_host());
+
+  // Verify that RenderWidgetHostViews have updated to be
+  // RenderWidgetHostViewChildFrame.
+  {
+    auto* rwhv_a = static_cast<RenderWidgetHostViewBase*>(rfh_a->GetView());
+    ASSERT_TRUE(rwhv_a);
+    EXPECT_TRUE(rwhv_a->IsRenderWidgetHostViewChildFrame());
+    auto* rwhv_b = static_cast<RenderWidgetHostViewBase*>(rfh_b->GetView());
+    ASSERT_TRUE(rwhv_b);
+    EXPECT_TRUE(rwhv_b->IsRenderWidgetHostViewChildFrame());
+  }
+
+  // Let the navigation complete.
+  ASSERT_TRUE(nav_manager_b.WaitForNavigationFinished());
+  EXPECT_TRUE(WaitForLoadStop(inner_wc_impl));
+
+  // Verify the speculative rfh is switched to be current rfh.
+  ASSERT_EQ(rfh_b, inner_wc->GetPrimaryMainFrame());
+
+  // Verify that RenderWidgetHostViews is still RenderWidgetHostViewChildFrame.
+  {
+    auto* rwhv_b = static_cast<RenderWidgetHostViewBase*>(rfh_b->GetView());
+    ASSERT_TRUE(rwhv_b);
+    ASSERT_TRUE(rwhv_b->IsRenderWidgetHostViewChildFrame());
+  }
+}
+
 // Tests destroying the outer delegate node while there is an unowned inner
 // WebContents attached. Ensures that the inner WebContents' RFH is still alive
 // and the WebContents can be re-attached to the outer WebContents.
diff --git a/content/browser/webid/test/mock_permission_delegate.h b/content/browser/webid/test/mock_permission_delegate.h
index 0ac72d1..57e44de2 100644
--- a/content/browser/webid/test/mock_permission_delegate.h
+++ b/content/browser/webid/test/mock_permission_delegate.h
@@ -13,7 +13,6 @@
 #include "content/public/browser/webid/federated_identity_permission_context_delegate.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/common/webid/login_status_options.h"
-#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom-forward.h"
 #include "url/gurl.h"
 
 namespace content {
diff --git a/content/browser/webrtc/resources/peer_connection_update_table.js b/content/browser/webrtc/resources/peer_connection_update_table.js
index 121f1e0..ab2a10e7 100644
--- a/content/browser/webrtc/resources/peer_connection_update_table.js
+++ b/content/browser/webrtc/resources/peer_connection_update_table.js
@@ -130,12 +130,12 @@
         'transceiverModified'].includes(update.type)) {
       const data = JSON.parse(update.value);
       type += '(index=' + data.transceiverIndex + ', kind=' + data.kind + ')';
-    } else if (['iceconnectionstatechange', 'connectionstatechange',
-        'signalingstatechange'].includes(update.type)) {
+    } else if (['oniceconnectionstatechange', 'onconnectionstatechange',
+        'onsignalingstatechange'].includes(update.type)) {
       const fieldName = {
-        'iceconnectionstatechange' : 'iceconnectionstate',
-        'connectionstatechange' : 'connectionstate',
-        'signalingstatechange' : 'signalingstate',
+        'oniceconnectionstatechange' : 'iceconnectionstate',
+        'onconnectionstatechange' : 'connectionstate',
+        'onsignalingstatechange' : 'signalingstate',
       }[update.type];
       const el = peerConnectionElement.getElementsByClassName(fieldName)[0];
       const numberOfEvents = el.textContent.split(' => ').length;
@@ -156,9 +156,9 @@
     details.appendChild(valueContainer);
 
     // Highlight ICE/DTLS failures and failure callbacks.
-    if ((update.type === 'iceconnectionstatechange' &&
+    if ((update.type === 'oniceconnectionstatechange' &&
          update.value === '"failed"') ||
-        (update.type === 'connectionstatechange' &&
+        (update.type === 'onconnectionstatechange' &&
          update.value === '"failed"') ||
         update.type.indexOf('OnFailure') !== -1 ||
         update.type === 'addIceCandidateFailed') {
diff --git a/content/browser/webrtc/resources/stats_rates_calculator.js b/content/browser/webrtc/resources/stats_rates_calculator.js
index 02c6ece..8b89351 100644
--- a/content/browser/webrtc/resources/stats_rates_calculator.js
+++ b/content/browser/webrtc/resources/stats_rates_calculator.js
@@ -139,6 +139,7 @@
     if (!previousStats || !currentStats) {
       return undefined;
     }
+    // Timestamp is in milliseconds.
     const deltaTime = currentStats.timestamp - previousStats.timestamp;
     if (deltaTime <= 0) {
       return undefined;
@@ -159,6 +160,10 @@
     }
     const deltaValue = currentValue - previousValue;
     const deltaSamples = currentSamples - previousSamples;
+    if (samplesMetric === 'timestamp') {
+      // Timestamp is in milliseconds but we expect seconds as output.
+      return 1000 * deltaValue / deltaSamples;
+    }
     return deltaValue / deltaSamples;
   }
 }
@@ -305,6 +310,7 @@
     if (!previousStats || !currentStats) {
       return undefined;
     }
+    // Timestamp is in milliseconds.
     const deltaTime = currentStats.timestamp - previousStats.timestamp;
     if (deltaTime <= 0) {
       return undefined;
@@ -522,10 +528,12 @@
               }
               metricCalculators.forEach(metricCalculator => {
                 const name = metricCalculator.getCalculatedMetricName();
-                this.currentReport.get(stats.id)[name] =
-                    metricCalculator.calculate(stats.id,
+                const result = metricCalculator.calculate(stats.id,
                                                this.previousReport,
                                                this.currentReport);
+                if (!isNaN(result)) {
+                  this.currentReport.get(stats.id)[name] = result;
+                }
               });
             });
       });
diff --git a/content/browser/webrtc/webrtc_internals.cc b/content/browser/webrtc/webrtc_internals.cc
index f5e168f3..a2c617a7 100644
--- a/content/browser/webrtc/webrtc_internals.cc
+++ b/content/browser/webrtc/webrtc_internals.cc
@@ -229,7 +229,7 @@
   if (it == peer_connection_data().end())
     return;
 
-  if (type == "iceconnectionstatechange") {
+  if (type == "oniceconnectionstatechange") {
     if (value == "\"connected\"" || value == "\"checking\"" ||
         value == "\"completed\"") {
       MaybeMarkPeerConnectionAsConnected(*it);
diff --git a/content/browser/webrtc/webrtc_internals_unittest.cc b/content/browser/webrtc/webrtc_internals_unittest.cc
index 32682c2c..3e91bcb 100644
--- a/content/browser/webrtc/webrtc_internals_unittest.cc
+++ b/content/browser/webrtc/webrtc_internals_unittest.cc
@@ -688,8 +688,8 @@
     EXPECT_EQ(webrtc_internals.num_connected_connections(), 0);
     EXPECT_FALSE(webrtc_internals.HasWakeLock());
 
-    webrtc_internals.OnPeerConnectionUpdated(kFrameId, kLid,
-                                             "iceconnectionstatechange", value);
+    webrtc_internals.OnPeerConnectionUpdated(
+        kFrameId, kLid, "oniceconnectionstatechange", value);
     EXPECT_EQ(webrtc_internals.num_connected_connections(), 1);
     EXPECT_TRUE(webrtc_internals.HasWakeLock());
 
@@ -714,8 +714,8 @@
   // A sequence of connecting messages should not increase the number of
   // connected connections beyond 1.
   for (const char* value : kWakeLockConnectingValues) {
-    webrtc_internals.OnPeerConnectionUpdated(kFrameId, kLid,
-                                             "iceconnectionstatechange", value);
+    webrtc_internals.OnPeerConnectionUpdated(
+        kFrameId, kLid, "oniceconnectionstatechange", value);
     EXPECT_EQ(webrtc_internals.num_connected_connections(), 1);
     EXPECT_TRUE(webrtc_internals.HasWakeLock());
   }
@@ -739,12 +739,12 @@
     EXPECT_FALSE(webrtc_internals.HasWakeLock());
 
     webrtc_internals.OnPeerConnectionUpdated(
-        kFrameId, kLid, "iceconnectionstatechange", "\"connected\"");
+        kFrameId, kLid, "oniceconnectionstatechange", "\"connected\"");
     EXPECT_EQ(webrtc_internals.num_connected_connections(), 1);
     EXPECT_TRUE(webrtc_internals.HasWakeLock());
 
-    webrtc_internals.OnPeerConnectionUpdated(kFrameId, kLid,
-                                             "iceconnectionstatechange", value);
+    webrtc_internals.OnPeerConnectionUpdated(
+        kFrameId, kLid, "oniceconnectionstatechange", value);
     EXPECT_EQ(webrtc_internals.num_connected_connections(), 0);
     EXPECT_FALSE(webrtc_internals.HasWakeLock());
 
@@ -767,15 +767,15 @@
   EXPECT_FALSE(webrtc_internals.HasWakeLock());
 
   webrtc_internals.OnPeerConnectionUpdated(
-      kFrameId, kLid, "iceconnectionstatechange", "\"connected\"");
+      kFrameId, kLid, "oniceconnectionstatechange", "\"connected\"");
   EXPECT_EQ(webrtc_internals.num_connected_connections(), 1);
   EXPECT_TRUE(webrtc_internals.HasWakeLock());
 
   // A sequence of disconnecting messages should not decrease the number of
   // connected connections below zero.
   for (const char* value : kWakeLockDisconnectingValues) {
-    webrtc_internals.OnPeerConnectionUpdated(kFrameId, kLid,
-                                             "iceconnectionstatechange", value);
+    webrtc_internals.OnPeerConnectionUpdated(
+        kFrameId, kLid, "oniceconnectionstatechange", value);
     EXPECT_EQ(webrtc_internals.num_connected_connections(), 0);
     EXPECT_FALSE(webrtc_internals.HasWakeLock());
   }
@@ -798,17 +798,17 @@
   EXPECT_FALSE(webrtc_internals.HasWakeLock());
 
   webrtc_internals.OnPeerConnectionUpdated(
-      kFrameId, kLid, "iceconnectionstatechange", "\"connected\"");
+      kFrameId, kLid, "oniceconnectionstatechange", "\"connected\"");
   EXPECT_EQ(webrtc_internals.num_connected_connections(), 1);
   EXPECT_TRUE(webrtc_internals.HasWakeLock());
 
   webrtc_internals.OnPeerConnectionUpdated(
-      kFrameId, kLid, "iceconnectionstatechange", "\"disconnected\"");
+      kFrameId, kLid, "oniceconnectionstatechange", "\"disconnected\"");
   EXPECT_EQ(webrtc_internals.num_connected_connections(), 0);
   EXPECT_FALSE(webrtc_internals.HasWakeLock());
 
   webrtc_internals.OnPeerConnectionUpdated(
-      kFrameId, kLid, "iceconnectionstatechange", "\"connected\"");
+      kFrameId, kLid, "oniceconnectionstatechange", "\"connected\"");
   EXPECT_EQ(webrtc_internals.num_connected_connections(), 1);
   EXPECT_TRUE(webrtc_internals.HasWakeLock());
 
@@ -834,28 +834,28 @@
   }
 
   webrtc_internals.OnPeerConnectionUpdated(
-      kFrameId, kLids[0], "iceconnectionstatechange", "\"connected\"");
+      kFrameId, kLids[0], "oniceconnectionstatechange", "\"connected\"");
   EXPECT_EQ(webrtc_internals.num_connected_connections(), 1);
   EXPECT_TRUE(webrtc_internals.HasWakeLock());
 
   webrtc_internals.OnPeerConnectionUpdated(
-      kFrameId, kLids[1], "iceconnectionstatechange", "\"completed\"");
+      kFrameId, kLids[1], "oniceconnectionstatechange", "\"completed\"");
   EXPECT_EQ(webrtc_internals.num_connected_connections(), 2);
   EXPECT_TRUE(webrtc_internals.HasWakeLock());
 
   webrtc_internals.OnPeerConnectionUpdated(
-      kFrameId, kLids[2], "iceconnectionstatechange", "\"checking\"");
+      kFrameId, kLids[2], "oniceconnectionstatechange", "\"checking\"");
   EXPECT_EQ(webrtc_internals.num_connected_connections(), 3);
   EXPECT_TRUE(webrtc_internals.HasWakeLock());
 
   // A duplicate message should not alter the number of connected connections.
   webrtc_internals.OnPeerConnectionUpdated(
-      kFrameId, kLids[2], "iceconnectionstatechange", "\"checking\"");
+      kFrameId, kLids[2], "oniceconnectionstatechange", "\"checking\"");
   EXPECT_EQ(webrtc_internals.num_connected_connections(), 3);
   EXPECT_TRUE(webrtc_internals.HasWakeLock());
 
   webrtc_internals.OnPeerConnectionUpdated(
-      kFrameId, kLids[0], "iceconnectionstatechange", "\"closed\"");
+      kFrameId, kLids[0], "oniceconnectionstatechange", "\"closed\"");
   EXPECT_EQ(webrtc_internals.num_connected_connections(), 2);
   EXPECT_TRUE(webrtc_internals.HasWakeLock());
 
@@ -894,17 +894,17 @@
   EXPECT_EQ(0u, observer.latest_connections_count());
 
   webrtc_internals.OnPeerConnectionUpdated(
-      kFrameId, kLid, "iceconnectionstatechange", "\"connected\"");
+      kFrameId, kLid, "oniceconnectionstatechange", "\"connected\"");
   EXPECT_EQ(webrtc_internals.num_connected_connections(), 1);
   EXPECT_EQ(1u, observer.latest_connections_count());
 
   webrtc_internals.OnPeerConnectionUpdated(
-      kFrameId, kLid, "iceconnectionstatechange", "\"disconnected\"");
+      kFrameId, kLid, "oniceconnectionstatechange", "\"disconnected\"");
   EXPECT_EQ(webrtc_internals.num_connected_connections(), 0);
   EXPECT_EQ(0u, observer.latest_connections_count());
 
   webrtc_internals.OnPeerConnectionUpdated(
-      kFrameId, kLid, "iceconnectionstatechange", "\"connected\"");
+      kFrameId, kLid, "oniceconnectionstatechange", "\"connected\"");
   EXPECT_EQ(webrtc_internals.num_connected_connections(), 1);
   EXPECT_EQ(1u, observer.latest_connections_count());
 
diff --git a/content/browser/worker_host/mock_shared_worker.cc b/content/browser/worker_host/mock_shared_worker.cc
index e5f9b16a..2912612 100644
--- a/content/browser/worker_host/mock_shared_worker.cc
+++ b/content/browser/worker_host/mock_shared_worker.cc
@@ -147,7 +147,8 @@
     mojo::PendingReceiver<blink::mojom::ReportingObserver>
         coep_reporting_observer,
     mojo::PendingReceiver<blink::mojom::ReportingObserver>
-        dip_reporting_observer) {
+        dip_reporting_observer,
+    std::optional<blink::NoiseToken> canvas_noise_token) {
   DCHECK(!create_params_);
   create_params_ = std::make_unique<CreateParams>();
   create_params_->info = std::move(info);
diff --git a/content/browser/worker_host/mock_shared_worker.h b/content/browser/worker_host/mock_shared_worker.h
index 5a2235d8..1f72304 100644
--- a/content/browser/worker_host/mock_shared_worker.h
+++ b/content/browser/worker_host/mock_shared_worker.h
@@ -122,7 +122,8 @@
       mojo::PendingReceiver<blink::mojom::ReportingObserver>
           coep_reporting_observer,
       mojo::PendingReceiver<blink::mojom::ReportingObserver>
-          dip_reporting_observer) override;
+          dip_reporting_observer,
+      std::optional<blink::NoiseToken> canvas_noise_token) override;
 
   struct CreateParams {
     CreateParams();
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index 981bced6..25cd40f 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -17,6 +17,7 @@
 #include "content/browser/code_cache/generated_code_cache_context.h"
 #include "content/browser/devtools/devtools_instrumentation.h"
 #include "content/browser/devtools/shared_worker_devtools_manager.h"
+#include "content/browser/fingerprinting_protection/canvas_noise_token_data.h"
 #include "content/browser/loader/url_loader_factory_utils.h"
 #include "content/browser/network/cross_origin_embedder_policy_reporter.h"
 #include "content/browser/renderer_host/code_cache_host_impl.h"
@@ -55,6 +56,7 @@
 #include "services/network/public/mojom/ip_address_space.mojom-shared.h"
 #include "storage/browser/blob/blob_url_store_impl.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/fingerprinting_protection/noise_token.h"
 #include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
 #include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
@@ -425,6 +427,9 @@
     dip_reporter_->BindObserver(std::move(dip_reporting_remote));
   }
 
+  std::optional<blink::NoiseToken> canvas_noise_token =
+      GetOrCreateCanvasNoiseToken();
+
   // Send the CreateSharedWorker message.
   factory_.Bind(std::move(factory));
   factory_->CreateSharedWorker(
@@ -444,7 +449,8 @@
       receiver_.BindNewPipeAndPassRemote(), std::move(worker_receiver_),
       std::move(browser_interface_broker), ukm_source_id_,
       instance_.DoesRequireCrossSiteRequestForCookies(),
-      std::move(coep_reporting_observer), std::move(dip_reporting_observer));
+      std::move(coep_reporting_observer), std::move(dip_reporting_observer),
+      std::move(canvas_noise_token));
   if (service_worker_handle_->service_worker_client()) {
     service_worker_handle_->service_worker_client()->SetContainerReady();
   }
@@ -570,6 +576,21 @@
                                    token()};
 }
 
+std::optional<blink::NoiseToken>
+SharedWorkerHost::GetOrCreateCanvasNoiseToken() {
+  BrowserContext* browser_context = GetProcessHost()->GetBrowserContext();
+  GURL top_url = GetStorageKey().top_level_site().GetURL();
+
+  if (!GetContentClient()->browser()->ShouldEnableCanvasNoise(browser_context,
+                                                              top_url)) {
+    return std::nullopt;
+  }
+  // TODO(https://crbug.com/442616874): Use StorageKeys to call GetToken(), once
+  // CanvasNoiseTokens are keyed by StorageKey instead of Origin.
+  return CanvasNoiseTokenData::GetToken(browser_context,
+                                        url::Origin::Create(top_url));
+}
+
 void SharedWorkerHost::AllowFileSystem(
     const GURL& url,
     base::OnceCallback<void(bool)> callback) {
diff --git a/content/browser/worker_host/shared_worker_host.h b/content/browser/worker_host/shared_worker_host.h
index 5c8c65e..4f09243b 100644
--- a/content/browser/worker_host/shared_worker_host.h
+++ b/content/browser/worker_host/shared_worker_host.h
@@ -245,6 +245,9 @@
       blink::mojom::BucketHost::GetDirectoryCallback callback) override;
   storage::BucketClientInfo GetBucketClientInfo() const override;
 
+  // Helper to get or create a new canvas noise token for this worker.
+  std::optional<blink::NoiseToken> GetOrCreateCanvasNoiseToken();
+
  private:
   friend class SharedWorkerHostTest;
 
diff --git a/content/browser/xr/service/xr_device_service.cc b/content/browser/xr/service/xr_device_service.cc
index e78fcb0..2a3fba7 100644
--- a/content/browser/xr/service/xr_device_service.cc
+++ b/content/browser/xr/service/xr_device_service.cc
@@ -30,7 +30,8 @@
   // BindGpu is called from the XR process to establish a connection to the GPU
   // process.
   void BindGpu(::mojo::PendingReceiver<::viz::mojom::Gpu> receiver) override {
-    gpu_client_ = content::CreateGpuClient(std::move(receiver));
+    gpu_client_ = content::CreateGpuClient(
+        std::move(receiver), /*enable_extra_handles_validation=*/false);
   }
 
  private:
diff --git a/content/common/service_worker/service_worker_router_evaluator.h b/content/common/service_worker/service_worker_router_evaluator.h
index 48ef99d..27a9a9c 100644
--- a/content/common/service_worker/service_worker_router_evaluator.h
+++ b/content/common/service_worker/service_worker_router_evaluator.h
@@ -12,7 +12,6 @@
 #include "services/network/public/cpp/resource_request.h"
 #include "third_party/blink/public/common/service_worker/embedded_worker_status.h"
 #include "third_party/blink/public/common/service_worker/service_worker_router_rule.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker.mojom.h"
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
diff --git a/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java b/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java
index e84a55f..5185da1 100644
--- a/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java
@@ -268,6 +268,24 @@
     }
 
     @Override
+    public void performReportWebAuthSecurityChecks(
+            String relyingPartyId,
+            Origin effectiveOrigin,
+            Callback<WebAuthSecurityChecksResults> callback) {
+        if (mNativeRenderFrameHostAndroid == 0) {
+            var result =
+                    new WebAuthSecurityChecksResults(
+                            AuthenticatorStatus.UNKNOWN_ERROR, /* isCrossOrigin= */ false);
+            callback.onResult(result);
+            return;
+        }
+
+        RenderFrameHostImplJni.get()
+                .performReportWebAuthSecurityChecks(
+                        mNativeRenderFrameHostAndroid, relyingPartyId, effectiveOrigin, callback);
+    }
+
+    @Override
     public GlobalRenderFrameHostId getGlobalRenderFrameHostId() {
         return mRenderFrameHostId;
     }
@@ -360,6 +378,12 @@
                 @Nullable Origin remoteDesktopClientOverrideOrigin,
                 Callback<RenderFrameHost.WebAuthSecurityChecksResults> callback);
 
+        void performReportWebAuthSecurityChecks(
+                long nativeRenderFrameHostAndroid,
+                String relyingPartyId,
+                Origin effectiveOrigin,
+                Callback<RenderFrameHost.WebAuthSecurityChecksResults> callback);
+
         int getLifecycleState(long nativeRenderFrameHostAndroid);
 
         void insertVisualStateCallback(
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/SelectActionMenuHelper.java b/content/public/android/java/src/org/chromium/content/browser/selection/SelectActionMenuHelper.java
index 04504a2..44e5738 100644
--- a/content/public/android/java/src/org/chromium/content/browser/selection/SelectActionMenuHelper.java
+++ b/content/public/android/java/src/org/chromium/content/browser/selection/SelectActionMenuHelper.java
@@ -176,13 +176,17 @@
                         selectionActionMenuDelegate, classificationResult, selectedText);
         if (secondaryAssistItems != null) itemGroups.add(secondaryAssistItems);
 
-        itemGroups.add(
+        SelectionMenuGroup textProcessingAssistItems =
                 getTextProcessingItems(
                         context,
                         isSelectionPassword,
                         isSelectionReadOnly,
+                        selectedText,
                         textProcessingIntentHandler,
-                        selectionActionMenuDelegate));
+                        selectionActionMenuDelegate);
+        if (textProcessingAssistItems != null) {
+            itemGroups.add(textProcessingAssistItems);
+        }
 
         return itemGroups;
     }
@@ -302,12 +306,14 @@
     }
 
     @VisibleForTesting
-    /* package */ static SelectionMenuGroup getTextProcessingItems(
+    /* package */ static @Nullable SelectionMenuGroup getTextProcessingItems(
             Context context,
             boolean isSelectionPassword,
             boolean isSelectionReadOnly,
+            String selectedText,
             @Nullable TextProcessingIntentHandler intentHandler,
             @Nullable SelectionActionMenuDelegate selectionActionMenuDelegate) {
+        if (selectedText.isEmpty()) return null;
         SelectionMenuGroup textProcessingItems =
                 new SelectionMenuGroup(
                         R.id.select_action_menu_text_processing_items,
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
index 97181a71..5824aa5 100644
--- a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
@@ -1166,8 +1166,13 @@
 
         SelectionMenuGroup textProcessingItems =
                 SelectActionMenuHelper.getTextProcessingItems(
-                        mContext, false, false, this::processText, mSelectionActionMenuDelegate);
-        if (!textProcessingItems.items.isEmpty()) {
+                        mContext,
+                        false,
+                        false,
+                        "test",
+                        this::processText,
+                        mSelectionActionMenuDelegate);
+        if (textProcessingItems != null && !textProcessingItems.items.isEmpty()) {
             addMenuItemsToActionMenu(
                     mContext, textProcessingItems, menu, mCustomActionMenuItemClickListeners, null);
         }
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java b/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java
index 92396dc8..18e1976 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java
@@ -175,13 +175,13 @@
             Callback<WebAuthSecurityChecksResults> callback);
 
     /**
-     * Runs security checks associated with a Web Authentication MakeCredential request for the the
+     * Runs security checks associated with a Web Authentication MakeCredential request for the
      * given relying party ID, an effective origin and whether MakeCredential is making the payment
      * credential. See performGetAssertionWebAuthSecurityChecks for more on |effectiveOrigin|.
      *
      * <p>This operation may trigger network fetches and thus it takes a `Callback`. The argument to
      * the callback is an object containing (1) the status code indicating the result of the
-     * GetAssertion request security checks, and (2) whether the effectiveOrigin is a cross-origin
+     * MakeCredential request security checks, and (2) whether the effectiveOrigin is a cross-origin
      * with any frame in this frame's ancestor chain.
      *
      * <p>`remoteDesktopClientOverrideOrigin` is the origin from the RemoteDesktopClientOverride
@@ -195,6 +195,21 @@
             Callback<WebAuthSecurityChecksResults> callback);
 
     /**
+     * Runs security checks associated with a Web Authentication Report request for the given
+     * relying party ID and an effective origin. See performGetAssertionWebAuthSecurityChecks for
+     * more on |effectiveOrigin|.
+     *
+     * <p>This operation may trigger network fetches and thus it takes a `Callback`. The argument to
+     * the callback is an object containing (1) the status code indicating the result of the Report
+     * request security checks, and (2) whether the effectiveOrigin is a cross-origin with any frame
+     * in this frame's ancestor chain.
+     */
+    void performReportWebAuthSecurityChecks(
+            String relyingPartyId,
+            Origin effectiveOrigin,
+            Callback<WebAuthSecurityChecksResults> callback);
+
+    /**
      * @return An identifier for this RenderFrameHost.
      */
     GlobalRenderFrameHostId getGlobalRenderFrameHostId();
diff --git a/content/public/android/junit/src/org/chromium/content/browser/selection/SelectActionMenuHelperTest.java b/content/public/android/junit/src/org/chromium/content/browser/selection/SelectActionMenuHelperTest.java
index 443e9201..04e9e73 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/selection/SelectActionMenuHelperTest.java
+++ b/content/public/android/junit/src/org/chromium/content/browser/selection/SelectActionMenuHelperTest.java
@@ -5,6 +5,7 @@
 package org.chromium.content.browser.selection;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
@@ -185,15 +186,29 @@
                 new TestSelectionActionMenuDelegate();
         SelectionMenuGroup group =
                 SelectActionMenuHelper.getTextProcessingItems(
-                        mContext, false, true, intentHandler, null);
+                        mContext, false, true, "test", intentHandler, null);
         assertEquals(1, group.items.size());
 
         group =
                 SelectActionMenuHelper.getTextProcessingItems(
-                        mContext, false, true, intentHandler, selectionActionMenuDelegate);
+                        mContext, false, true, "test", intentHandler, selectionActionMenuDelegate);
         assertEquals(0, group.items.size());
     }
 
+    @Test
+    @Feature({"TextInput"})
+    public void testGetTextProcessingItems_emptySelection() {
+        SelectActionMenuHelper.TextProcessingIntentHandler intentHandler =
+                new SelectActionMenuHelper.TextProcessingIntentHandler() {
+                    @Override
+                    public void handleIntent(Intent textProcessingIntent) {}
+                };
+        SelectionMenuGroup group =
+                SelectActionMenuHelper.getTextProcessingItems(
+                        mContext, false, true, "", intentHandler, null);
+        assertNull(group);
+    }
+
     private ResolveInfo createResolveInfoWithActivityInfo(String activityName, boolean exported) {
         String packageName = "org.chromium.content.browser.selection.SelectActionMenuHelperTest";
 
diff --git a/content/public/browser/browser_xr_runtime.h b/content/public/browser/browser_xr_runtime.h
index e4041ef2..3c7199e2 100644
--- a/content/public/browser/browser_xr_runtime.h
+++ b/content/public/browser/browser_xr_runtime.h
@@ -7,7 +7,6 @@
 
 #include "base/observer_list_types.h"
 #include "content/common/content_export.h"
-#include "device/vr/public/mojom/vr_service.mojom.h"
 
 namespace content {
 
diff --git a/content/public/browser/client_hints.h b/content/public/browser/client_hints.h
index d707e70..dee19f1 100644
--- a/content/public/browser/client_hints.h
+++ b/content/public/browser/client_hints.h
@@ -8,7 +8,6 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/client_hints_controller_delegate.h"
 #include "net/http/http_request_headers.h"
-#include "services/network/public/mojom/parsed_headers.mojom-forward.h"
 
 namespace content {
 
diff --git a/content/public/browser/desktop_media_id.h b/content/public/browser/desktop_media_id.h
index b90a88a..b10c5376 100644
--- a/content/public/browser/desktop_media_id.h
+++ b/content/public/browser/desktop_media_id.h
@@ -20,6 +20,7 @@
 struct CONTENT_EXPORT DesktopMediaID {
  public:
   enum Type { TYPE_NONE, TYPE_SCREEN, TYPE_WINDOW, TYPE_WEB_CONTENTS };
+  enum class AudioType { kNone, kSystem, kApplication };
 
   using Id = intptr_t;
 
@@ -75,6 +76,8 @@
 
   // This records whether the desktop share has sound or not.
   bool audio_share = false;
+  // This records the type of audio share, if any.
+  AudioType window_audio_type = AudioType::kNone;
 };
 
 }  // namespace content
diff --git a/content/public/browser/devtools_agent_host.h b/content/public/browser/devtools_agent_host.h
index 6363cd2..5e6433f5 100644
--- a/content/public/browser/devtools_agent_host.h
+++ b/content/public/browser/devtools_agent_host.h
@@ -19,8 +19,6 @@
 #include "content/public/browser/devtools_agent_host_client.h"
 #include "content/public/browser/devtools_agent_host_observer.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "services/network/public/mojom/network_context.mojom-forward.h"
-#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 #include "url/gurl.h"
 
 namespace base {
diff --git a/content/public/browser/download_manager.h b/content/public/browser/download_manager.h
index 3cdf8ed..59be269 100644
--- a/content/public/browser/download_manager.h
+++ b/content/public/browser/download_manager.h
@@ -40,7 +40,6 @@
 #include "base/time/time.h"
 #include "components/download/public/common/download_interrupt_reasons.h"
 #include "components/download/public/common/download_item.h"
-#include "components/download/public/common/download_stream.mojom-forward.h"
 #include "components/download/public/common/download_url_parameters.h"
 #include "components/download/public/common/input_stream.h"
 #include "components/download/public/common/simple_download_manager.h"
diff --git a/content/public/browser/gpu_client.h b/content/public/browser/gpu_client.h
index 522f68f..82a5465 100644
--- a/content/public/browser/gpu_client.h
+++ b/content/public/browser/gpu_client.h
@@ -16,7 +16,8 @@
 
 CONTENT_EXPORT
 std::unique_ptr<viz::GpuClient, base::OnTaskRunnerDeleter> CreateGpuClient(
-    mojo::PendingReceiver<viz::mojom::Gpu> receiver);
+    mojo::PendingReceiver<viz::mojom::Gpu> receiver,
+    bool enable_extra_handles_validation);
 
 }  // namespace content
 
diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h
index 32bcee1..57e85c2 100644
--- a/content/public/browser/render_process_host.h
+++ b/content/public/browser/render_process_host.h
@@ -26,7 +26,6 @@
 #include "content/public/browser/child_process_id.h"
 #include "content/public/browser/web_exposed_isolation_level.h"
 #include "ipc/ipc_listener.h"
-#include "ipc/ipc_sender.h"
 #include "media/media_buildflags.h"
 #include "media/mojo/mojom/video_decode_perf_history.mojom-forward.h"
 #include "mojo/public/cpp/bindings/generic_pending_receiver.h"
@@ -128,8 +127,7 @@
 // Interface that represents the browser side of the browser <-> renderer
 // communication channel. There will generally be one RenderProcessHost per
 // renderer process.
-class CONTENT_EXPORT RenderProcessHost : public IPC::Sender,
-                                         public IPC::Listener,
+class CONTENT_EXPORT RenderProcessHost : public IPC::Listener,
                                          public base::SupportsUserData {
   // Do not remove this macro!
   // The macro is maintained by the memory safety team.
diff --git a/content/public/browser/render_view_host.h b/content/public/browser/render_view_host.h
index a599bc3..782bed0 100644
--- a/content/public/browser/render_view_host.h
+++ b/content/public/browser/render_view_host.h
@@ -10,7 +10,6 @@
 #include "content/public/common/drop_data.h"
 #include "content/public/common/page_zoom.h"
 #include "third_party/blink/public/common/page/drag_operation.h"
-#include "third_party/blink/public/mojom/frame/frame.mojom-forward.h"
 
 namespace perfetto::protos::pbzero {
 class RenderViewHost;
diff --git a/content/public/browser/render_widget_host.h b/content/public/browser/render_widget_host.h
index 8ab6477..46d770867 100644
--- a/content/public/browser/render_widget_host.h
+++ b/content/public/browser/render_widget_host.h
@@ -198,9 +198,6 @@
   // true if visual properties have changed since last call.
   virtual bool SynchronizeVisualProperties() = 0;
 
-  // Access to the implementation's IPC::Listener::OnMessageReceived. Intended
-  // only for test code.
-
   // Add/remove a callback that can handle key presses without requiring focus.
   using KeyPressEventCallback =
       base::RepeatingCallback<bool(const input::NativeWebKeyboardEvent&)>;
diff --git a/content/public/browser/select_audio_output_request.h b/content/public/browser/select_audio_output_request.h
index 0b77cbb..be6a40e0 100644
--- a/content/public/browser/select_audio_output_request.h
+++ b/content/public/browser/select_audio_output_request.h
@@ -15,7 +15,6 @@
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/render_frame_host.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
-#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 namespace content {
 
diff --git a/content/public/browser/service_process_host.cc b/content/public/browser/service_process_host.cc
index d1bc550..99389bc 100644
--- a/content/public/browser/service_process_host.cc
+++ b/content/public/browser/service_process_host.cc
@@ -70,9 +70,12 @@
 #endif  // #if BUILDFLAG(IS_WIN)
 
 ServiceProcessHost::Options& ServiceProcessHost::Options::WithGpuClient(
+    bool enable_extra_handles_validation,
     base::PassKey<ServiceProcessHostGpuClient> passkey) {
-#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
+#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) || \
+    BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
   allow_gpu_client = true;
+  extra_handles_validation = enable_extra_handles_validation;
 #endif  // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
   return *this;
 }
diff --git a/content/public/browser/service_process_host.h b/content/public/browser/service_process_host.h
index 0062d2c..30cb197 100644
--- a/content/public/browser/service_process_host.h
+++ b/content/public/browser/service_process_host.h
@@ -116,7 +116,8 @@
     // platforms where that is supported. This option will be removed in future.
     // Prefer to avoid setting this option and instead bind the client directly
     // by passing a `pending_receiver<viz.mojom.Gpu>` to the service via mojo.
-    Options& WithGpuClient(base::PassKey<ServiceProcessHostGpuClient> passkey);
+    Options& WithGpuClient(bool enable_extra_handles_validation,
+                           base::PassKey<ServiceProcessHostGpuClient> passkey);
 
     // Passes the contents of this Options object to a newly returned Options
     // value. This must be called when moving a built Options object into a call
@@ -132,6 +133,7 @@
     std::vector<base::FilePath> preload_libraries;
 #endif  // BUILDFLAG(IS_WIN)
     std::optional<bool> allow_gpu_client;
+    std::optional<bool> extra_handles_validation;
   };
 
   // An interface which can be implemented and registered/unregistered with
diff --git a/content/public/browser/service_process_host_passkeys.h b/content/public/browser/service_process_host_passkeys.h
index 8a5ce34..8a432c5 100644
--- a/content/public/browser/service_process_host_passkeys.h
+++ b/content/public/browser/service_process_host_passkeys.h
@@ -30,6 +30,7 @@
 
 namespace content {
 class VideoCaptureServiceLauncher;
+class OOPVideoDecoderFactoryProcessLauncher;
 shape_detection::mojom::ShapeDetectionService* GetShapeDetectionService();
 
 class ServiceProcessHostPreloadLibraries {
@@ -69,6 +70,7 @@
   // Service launchers using `ServiceProcessHost::Options::WithGpuClient`
   // should be added here and must be reviewed by the security team.
   friend class content::VideoCaptureServiceLauncher;
+  friend class content::OOPVideoDecoderFactoryProcessLauncher;
 };
 
 }  // namespace content
diff --git a/content/public/browser/web_contents_observer.cc b/content/public/browser/web_contents_observer.cc
index ef0fc71..dbaba54 100644
--- a/content/public/browser/web_contents_observer.cc
+++ b/content/public/browser/web_contents_observer.cc
@@ -39,12 +39,6 @@
   }
 }
 
-bool WebContentsObserver::OnMessageReceived(
-    const IPC::Message& message,
-    RenderFrameHost* render_frame_host) {
-  return false;
-}
-
 void WebContentsObserver::ResetWebContents() {
   static_cast<WebContentsImpl*>(web_contents_)->RemoveObserver(this);
   web_contents_ = nullptr;
diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h
index 62e5f908..4faff4e4 100644
--- a/content/public/browser/web_contents_observer.h
+++ b/content/public/browser/web_contents_observer.h
@@ -911,10 +911,6 @@
   virtual void OnTextCopiedToClipboard(RenderFrameHost* render_frame_host,
                                        const std::u16string& copied_text) {}
 
-  // Invoked if an IPC message is coming from a specific RenderFrameHost.
-  virtual bool OnMessageReceived(const IPC::Message& message,
-                                 RenderFrameHost* render_frame_host);
-
   // Notification that the |render_widget_host| for this WebContents has gained
   // focus.
   virtual void OnWebContentsFocused(RenderWidgetHost* render_widget_host) {}
diff --git a/content/public/browser/webid/federated_identity_permission_context_delegate.h b/content/public/browser/webid/federated_identity_permission_context_delegate.h
index 09008d8..e694ced 100644
--- a/content/public/browser/webid/federated_identity_permission_context_delegate.h
+++ b/content/public/browser/webid/federated_identity_permission_context_delegate.h
@@ -16,7 +16,6 @@
 #include "content/public/browser/webid/identity_request_account.h"
 #include "third_party/blink/public/common/webid/login_status_account.h"
 #include "third_party/blink/public/common/webid/login_status_options.h"
-#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom-forward.h"
 #include "url/origin.h"
 
 namespace content {
diff --git a/content/public/renderer/key_system_support.cc b/content/public/renderer/key_system_support.cc
index 7d79cfa..40e466b 100644
--- a/content/public/renderer/key_system_support.cc
+++ b/content/public/renderer/key_system_support.cc
@@ -8,6 +8,7 @@
 #include "content/public/renderer/render_thread.h"
 #include "content/renderer/render_frame_impl.h"
 #include "media/base/key_systems_support_registration.h"
+#include "media/mojo/mojom/key_system_support.mojom.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
diff --git a/content/public/renderer/key_system_support.h b/content/public/renderer/key_system_support.h
index f4a2e6b..534d6f7 100644
--- a/content/public/renderer/key_system_support.h
+++ b/content/public/renderer/key_system_support.h
@@ -13,7 +13,6 @@
 #include "content/public/renderer/render_frame.h"
 #include "media/base/key_system_capability.h"
 #include "media/base/key_systems_support_registration.h"
-#include "media/mojo/mojom/key_system_support.mojom.h"
 
 namespace content {
 
diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h
index c5643723..e45cb7d 100644
--- a/content/public/renderer/render_frame.h
+++ b/content/public/renderer/render_frame.h
@@ -17,6 +17,7 @@
 #include "content/common/buildflags.h"
 #include "content/common/content_export.h"
 #include "content/public/common/bindings_policy.h"
+#include "services/network/public/cpp/url_loader_completion_status.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "third_party/blink/public/common/subresource_load_metrics.h"
 #include "third_party/blink/public/common/use_counter/use_counter_feature.h"
@@ -243,6 +244,25 @@
   virtual void SetLoadFromMemoryCacheCallback(
       LoadFromMemoryCacheCallback callback) = 0;
 
+  // Sets the callback which is called when the renderer observes a new response
+  // start, completion, and cancellation.
+  using DidStartResponseCallback = base::RepeatingCallback<void(
+      const url::SchemeHostPort& final_response_url,
+      int request_id,
+      const network::mojom::URLResponseHead& response_head,
+      network::mojom::RequestDestination request_destination,
+      bool is_ad_resource)>;
+  using DidCompleteResponseCallback = base::RepeatingCallback<
+      void(int request_id, const network::URLLoaderCompletionStatus& status)>;
+  using DidCancelResponseCallback =
+      base::RepeatingCallback<void(int request_id)>;
+  virtual void SetDidStartResponseCallback(
+      DidStartResponseCallback callback) = 0;
+  virtual void SetDidCompleteResponseCallback(
+      DidCompleteResponseCallback callback) = 0;
+  virtual void SetDidCancelResponseCallback(
+      DidCancelResponseCallback callback) = 0;
+
  protected:
   ~RenderFrame() override {}
 
diff --git a/content/public/renderer/render_frame_observer.h b/content/public/renderer/render_frame_observer.h
index 9728f3c..5196f15 100644
--- a/content/public/renderer/render_frame_observer.h
+++ b/content/public/renderer/render_frame_observer.h
@@ -275,6 +275,10 @@
   // Notification when the renderer a response started, completed or canceled.
   // Complete or Cancel is guaranteed to be called for a response that started.
   // |request_id| uniquely identifies the request within this render frame.
+  //
+  // TODO(crbug.com/404425954): `DidStartResponse()`, `DidCompleteResponse()`,
+  // `DidCancelResponse()` are going to be deprecated. Use callback setters in
+  // `RenderFrame` instead.
   virtual void DidStartResponse(
       const url::SchemeHostPort& final_response_url,
       int request_id,
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockRenderFrameHost.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockRenderFrameHost.java
index 396c2f0..3cb46471 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockRenderFrameHost.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/mock/MockRenderFrameHost.java
@@ -111,6 +111,14 @@
     }
 
     @Override
+    public void performReportWebAuthSecurityChecks(
+            String relyingPartyId,
+            Origin effectiveOrigin,
+            Callback<WebAuthSecurityChecksResults> callback) {
+        callback.onResult(new WebAuthSecurityChecksResults(AuthenticatorStatus.SUCCESS, false));
+    }
+
+    @Override
     public GlobalRenderFrameHostId getGlobalRenderFrameHostId() {
         return new GlobalRenderFrameHostId(-1, -1);
     }
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc
index 26071ce..cba4e67 100644
--- a/content/public/test/mock_render_process_host.cc
+++ b/content/public/test/mock_render_process_host.cc
@@ -279,11 +279,6 @@
   return is_ready_;
 }
 
-bool MockRenderProcessHost::Send(IPC::Message* msg) {
-  delete msg;
-  return true;
-}
-
 ChildProcessId MockRenderProcessHost::GetID() const {
   return id_;
 }
@@ -639,13 +634,6 @@
   return base::NullCallback();
 }
 
-bool MockRenderProcessHost::OnMessageReceived(const IPC::Message& msg) {
-  IPC::Listener* listener = listeners_.Lookup(msg.routing_id());
-  if (listener)
-    return listener->OnMessageReceived(msg);
-  return false;
-}
-
 void MockRenderProcessHost::OnChannelConnected(int32_t peer_pid) {}
 
 void MockRenderProcessHost::OverrideBinderForTesting(
diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h
index 4d300bc..cd046b6f 100644
--- a/content/public/test/mock_render_process_host.h
+++ b/content/public/test/mock_render_process_host.h
@@ -285,11 +285,7 @@
   void ResumeSocketManagerForRenderFrameHost(
       const GlobalRenderFrameHostId& render_frame_host_id) override {}
 
-  // IPC::Sender via RenderProcessHost.
-  bool Send(IPC::Message* msg) override;
-
   // IPC::Listener via RenderProcessHost.
-  bool OnMessageReceived(const IPC::Message& msg) override;
   void OnChannelConnected(int32_t peer_pid) override;
 
   void set_priority(base::Process::Priority priority) { priority_ = priority; }
diff --git a/content/public/test/url_loader_monitor.h b/content/public/test/url_loader_monitor.h
index bc722b5..f820096 100644
--- a/content/public/test/url_loader_monitor.h
+++ b/content/public/test/url_loader_monitor.h
@@ -16,7 +16,6 @@
 #include "base/thread_annotations.h"
 #include "content/public/test/url_loader_interceptor.h"
 #include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/mojom/url_loader_completion_status.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
diff --git a/content/renderer/accessibility/render_accessibility_impl_test.h b/content/renderer/accessibility/render_accessibility_impl_test.h
index e9a715c6..0925df6 100644
--- a/content/renderer/accessibility/render_accessibility_impl_test.h
+++ b/content/renderer/accessibility/render_accessibility_impl_test.h
@@ -9,7 +9,6 @@
 
 #include "base/memory/raw_ptr.h"
 #include "content/public/test/render_view_test.h"
-#include "third_party/blink/public/mojom/render_accessibility.mojom.h"
 #include "third_party/blink/public/web/web_ax_object.h"
 #include "ui/accessibility/ax_location_and_scroll_updates.h"
 #include "ui/accessibility/ax_tree_update_forward.h"
diff --git a/content/renderer/agent_scheduling_group.cc b/content/renderer/agent_scheduling_group.cc
index 8a29eed..b262818 100644
--- a/content/renderer/agent_scheduling_group.cc
+++ b/content/renderer/agent_scheduling_group.cc
@@ -161,10 +161,10 @@
 
 AgentSchedulingGroup::~AgentSchedulingGroup() = default;
 
-void AgentSchedulingGroup::OnBadMessageReceived(const IPC::Message& message) {
+void AgentSchedulingGroup::OnBadMessageReceived() {
   // Not strictly required, since we don't currently do anything with bad
   // messages in the renderer, but if we ever do then this will "just work".
-  return ToImpl(*render_thread_).OnBadMessageReceived(message);
+  return ToImpl(*render_thread_).OnBadMessageReceived();
 }
 
 void AgentSchedulingGroup::OnAssociatedInterfaceRequest(
diff --git a/content/renderer/agent_scheduling_group.h b/content/renderer/agent_scheduling_group.h
index cde2028..38fd7a10 100644
--- a/content/renderer/agent_scheduling_group.h
+++ b/content/renderer/agent_scheduling_group.h
@@ -30,7 +30,6 @@
 #include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
 
 namespace IPC {
-class Message;
 class SyncChannel;
 }  // namespace IPC
 
@@ -92,7 +91,7 @@
 
  private:
   // IPC::Listener:
-  void OnBadMessageReceived(const IPC::Message& message) override;
+  void OnBadMessageReceived() override;
   void OnAssociatedInterfaceRequest(
       const std::string& interface_name,
       mojo::ScopedInterfaceEndpointHandle handle) override;
diff --git a/content/renderer/media/win/dcomp_texture_factory.h b/content/renderer/media/win/dcomp_texture_factory.h
index 90b5f99..712344f4 100644
--- a/content/renderer/media/win/dcomp_texture_factory.h
+++ b/content/renderer/media/win/dcomp_texture_factory.h
@@ -16,7 +16,6 @@
 #include "content/common/content_export.h"
 #include "content/renderer/media/win/dcomp_texture_host.h"
 #include "gpu/command_buffer/common/mailbox.h"
-#include "gpu/ipc/common/gpu_channel.mojom.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 
 namespace gpu {
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 9d568189..4971952a 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -4551,22 +4551,38 @@
     network::mojom::URLResponseHeadPtr response_head,
     network::mojom::RequestDestination request_destination,
     bool is_ad_resource) {
-  for (auto& observer : observers_) {
-    observer.DidStartResponse(final_response_url, request_id, *response_head,
-                              request_destination, is_ad_resource);
+  if (did_start_response_callback_) {
+    did_start_response_callback_.Run(final_response_url, request_id,
+                                     *response_head, request_destination,
+                                     is_ad_resource);
+  } else {
+    for (auto& observer : observers_) {
+      observer.DidStartResponse(final_response_url, request_id, *response_head,
+                                request_destination, is_ad_resource);
+    }
   }
 }
 
 void RenderFrameImpl::DidCompleteResponse(
     int request_id,
     const network::URLLoaderCompletionStatus& status) {
-  for (auto& observer : observers_)
-    observer.DidCompleteResponse(request_id, status);
+  if (did_complete_response_callback_) {
+    did_complete_response_callback_.Run(request_id, status);
+  } else {
+    for (auto& observer : observers_) {
+      observer.DidCompleteResponse(request_id, status);
+    }
+  }
 }
 
 void RenderFrameImpl::DidCancelResponse(int request_id) {
-  for (auto& observer : observers_)
-    observer.DidCancelResponse(request_id);
+  if (did_cancel_response_callback_) {
+    did_cancel_response_callback_.Run(request_id);
+  } else {
+    for (auto& observer : observers_) {
+      observer.DidCancelResponse(request_id);
+    }
+  }
 }
 
 void RenderFrameImpl::DidReceiveTransferSizeUpdate(int resource_id,
@@ -4639,6 +4655,21 @@
   load_from_memory_cache_callback_ = std::move(callback);
 }
 
+void RenderFrameImpl::SetDidStartResponseCallback(
+    DidStartResponseCallback callback) {
+  did_start_response_callback_ = std::move(callback);
+}
+
+void RenderFrameImpl::SetDidCompleteResponseCallback(
+    DidCompleteResponseCallback callback) {
+  did_complete_response_callback_ = std::move(callback);
+}
+
+void RenderFrameImpl::SetDidCancelResponseCallback(
+    DidCancelResponseCallback callback) {
+  did_cancel_response_callback_ = std::move(callback);
+}
+
 void RenderFrameImpl::DidObserveNewFeatureUsage(
     const blink::UseCounterFeature& feature) {
   TRACE_EVENT_WITH_FLOW0("navigation",
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index a87e628d..1b91ce9 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -780,6 +780,11 @@
   void SetSubresourceLoadCallback(SubresourceLoadCallback callback) override;
   void SetLoadFromMemoryCacheCallback(
       LoadFromMemoryCacheCallback callback) override;
+  void SetDidStartResponseCallback(DidStartResponseCallback callback) override;
+  void SetDidCompleteResponseCallback(
+      DidCompleteResponseCallback callback) override;
+  void SetDidCancelResponseCallback(
+      DidCancelResponseCallback callback) override;
 
  protected:
   explicit RenderFrameImpl(CreateParams params);
@@ -1260,6 +1265,11 @@
   // The callback to send the loaded resource info from memory cache to the
   // browser process through PageLoadMetrics.
   LoadFromMemoryCacheCallback load_from_memory_cache_callback_;
+  // The callback to handle response. These are used in `DidStartResponse()`,
+  // `DidCompleteResponse()`, and `DidCancelResponse()`, respectively.
+  DidStartResponseCallback did_start_response_callback_;
+  DidCompleteResponseCallback did_complete_response_callback_;
+  DidCancelResponseCallback did_cancel_response_callback_;
 
   // The text selection the last time DidChangeSelection got called. May contain
   // additional characters before and after the selected text, for IMEs. The
diff --git a/content/renderer/worker/embedded_shared_worker_stub.cc b/content/renderer/worker/embedded_shared_worker_stub.cc
index fa153da..1545abf 100644
--- a/content/renderer/worker/embedded_shared_worker_stub.cc
+++ b/content/renderer/worker/embedded_shared_worker_stub.cc
@@ -66,6 +66,7 @@
         coep_reporting_observer,
     mojo::PendingReceiver<blink::mojom::ReportingObserver>
         dip_reporting_observer,
+    std::optional<blink::NoiseToken> canvas_noise_token,
     const std::vector<std::string>& cors_exempt_header_list)
     : receiver_(this, std::move(receiver)) {
   DCHECK(main_script_load_params);
@@ -139,7 +140,8 @@
       ToWebPolicyContainer(std::move(policy_container)),
       std::move(web_worker_fetch_context), std::move(host), this, ukm_source_id,
       require_cross_site_request_for_cookies,
-      std::move(coep_reporting_observer), std::move(dip_reporting_observer));
+      std::move(coep_reporting_observer), std::move(dip_reporting_observer),
+      std::move(canvas_noise_token));
 
   // If the host drops its connection, then self-destruct.
   receiver_.set_disconnect_handler(base::BindOnce(
diff --git a/content/renderer/worker/embedded_shared_worker_stub.h b/content/renderer/worker/embedded_shared_worker_stub.h
index 398be69b..c21941d3 100644
--- a/content/renderer/worker/embedded_shared_worker_stub.h
+++ b/content/renderer/worker/embedded_shared_worker_stub.h
@@ -16,6 +16,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
+#include "third_party/blink/public/common/fingerprinting_protection/noise_token.h"
 #include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
@@ -88,6 +89,7 @@
           coep_reporting_observer,
       mojo::PendingReceiver<blink::mojom::ReportingObserver>
           dip_reporting_observer,
+      std::optional<blink::NoiseToken> canvas_noise_token,
       const std::vector<std::string>& cors_exempt_header_list);
 
   EmbeddedSharedWorkerStub(const EmbeddedSharedWorkerStub&) = delete;
diff --git a/content/renderer/worker/shared_worker_factory_impl.cc b/content/renderer/worker/shared_worker_factory_impl.cc
index a6ae76bf..fea774d 100644
--- a/content/renderer/worker/shared_worker_factory_impl.cc
+++ b/content/renderer/worker/shared_worker_factory_impl.cc
@@ -53,7 +53,8 @@
     mojo::PendingReceiver<blink::mojom::ReportingObserver>
         coep_reporting_observer,
     mojo::PendingReceiver<blink::mojom::ReportingObserver>
-        dip_reporting_observer) {
+        dip_reporting_observer,
+    std::optional<blink::NoiseToken> canvas_noise_token) {
   // Bound to the lifetime of the underlying blink::WebSharedWorker instance.
   new EmbeddedSharedWorkerStub(
       std::move(info), token, constructor_key, origin,
@@ -67,6 +68,7 @@
       std::move(browser_interface_broker), ukm_source_id,
       require_cross_site_request_for_cookies,
       std::move(coep_reporting_observer), std::move(dip_reporting_observer),
+      std::move(canvas_noise_token),
       RenderThreadImpl::current()->cors_exempt_header_list());
 }
 
diff --git a/content/renderer/worker/shared_worker_factory_impl.h b/content/renderer/worker/shared_worker_factory_impl.h
index 5c3f024..22bf9766 100644
--- a/content/renderer/worker/shared_worker_factory_impl.h
+++ b/content/renderer/worker/shared_worker_factory_impl.h
@@ -8,6 +8,7 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
+#include "third_party/blink/public/common/fingerprinting_protection/noise_token.h"
 #include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/frame/reporting_observer.mojom.h"
@@ -63,7 +64,8 @@
       mojo::PendingReceiver<blink::mojom::ReportingObserver>
           coep_reporting_observer,
       mojo::PendingReceiver<blink::mojom::ReportingObserver>
-          dip_reporting_observer) override;
+          dip_reporting_observer,
+      std::optional<blink::NoiseToken> canvas_noise_token) override;
 };
 
 }  // namespace content
diff --git a/content/services/auction_worklet/execution_mode_util.cc b/content/services/auction_worklet/execution_mode_util.cc
index 4a1ea514..ebf85cc 100644
--- a/content/services/auction_worklet/execution_mode_util.cc
+++ b/content/services/auction_worklet/execution_mode_util.cc
@@ -10,6 +10,7 @@
 #include "content/services/auction_worklet/auction_v8_helper.h"
 #include "content/services/auction_worklet/public/cpp/auction_worklet_features.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom.h"
 
 namespace auction_worklet {
 
diff --git a/content/services/auction_worklet/execution_mode_util.h b/content/services/auction_worklet/execution_mode_util.h
index 2177acf..d42e3bb 100644
--- a/content/services/auction_worklet/execution_mode_util.h
+++ b/content/services/auction_worklet/execution_mode_util.h
@@ -9,7 +9,7 @@
 #include "base/types/optional_ref.h"
 #include "content/common/content_export.h"
 #include "content/services/auction_worklet/context_recycler.h"
-#include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom.h"
+#include "third_party/blink/public/common/interest_group/interest_group.h"
 #include "url/origin.h"
 
 namespace auction_worklet {
diff --git a/content/services/auction_worklet/register_ad_macro_bindings.h b/content/services/auction_worklet/register_ad_macro_bindings.h
index ba35107..b43113f3ac 100644
--- a/content/services/auction_worklet/register_ad_macro_bindings.h
+++ b/content/services/auction_worklet/register_ad_macro_bindings.h
@@ -9,7 +9,6 @@
 #include "base/memory/raw_ptr.h"
 #include "content/services/auction_worklet/auction_v8_helper.h"
 #include "content/services/auction_worklet/context_recycler.h"
-#include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
 #include "v8/include/v8-forward.h"
 
 namespace auction_worklet {
diff --git a/content/shell/gpu/shell_content_gpu_client.h b/content/shell/gpu/shell_content_gpu_client.h
index abd25b45..a1e4d75 100644
--- a/content/shell/gpu/shell_content_gpu_client.h
+++ b/content/shell/gpu/shell_content_gpu_client.h
@@ -6,7 +6,6 @@
 #define CONTENT_SHELL_GPU_SHELL_CONTENT_GPU_CLIENT_H_
 
 #include "content/public/gpu/content_gpu_client.h"
-#include "services/network/public/mojom/network_service_test.mojom-forward.h"
 
 namespace content {
 
diff --git a/content/test/content_test_bundle_data.filelist b/content/test/content_test_bundle_data.filelist
index 7fb694c3..8c8e2cd 100644
--- a/content/test/content_test_bundle_data.filelist
+++ b/content/test/content_test_bundle_data.filelist
@@ -2032,6 +2032,8 @@
 data/accessibility/css/carousel-with-links-expected-android-external.txt
 data/accessibility/css/carousel-with-links-expected-blink.txt
 data/accessibility/css/carousel-with-links.html
+data/accessibility/css/carousel-with-tabs-dynamic-expected-blink.txt
+data/accessibility/css/carousel-with-tabs-dynamic.html
 data/accessibility/css/carousel-with-tabs-expected-android-assist-data.txt
 data/accessibility/css/carousel-with-tabs-expected-android-external.txt
 data/accessibility/css/carousel-with-tabs-expected-auralinux.txt
diff --git a/content/test/content_test_suite.cc b/content/test/content_test_suite.cc
index bbb7f2f..1b7fefb8 100644
--- a/content/test/content_test_suite.cc
+++ b/content/test/content_test_suite.cc
@@ -81,22 +81,6 @@
   // Reproducer:AttributionManagerImplTest.SendReport_RecordsTimeFromLastNavigation_Failure
   disable_field_trial("AttributionReportNavigationBasedRetry");
 
-  // TODO(447306270) Enable testing for this field trial feature.
-  // Reproducer:AuctionRunnerTest.OneBidOne404
-  disable_field_trial("FledgeSellerWorkletThreadPool");
-
-  // TODO(447306903) Enable testing for this field trial feature.
-  // Reproducer:AuctionRunnerTest.ExecutionModeGroupByOriginClickiness
-  disable_field_trial("FledgeBidderUseBalancingThreadSelector");
-
-  // TODO(447306271) Enable testing for this field trial feature.
-  // Reproducer:AuctionRunnerTest.PrivateAggregationRequestForEventFilteringIdNonKAnon
-  disable_field_trial("CookieDeprecationFacilitatedTesting");
-
-  // TODO(447306904) Enable testing for this field trial feature.
-  // Reproducer:AuctionRunnerTest.TrustedScoringSignalsJointBatchedRequests
-  disable_field_trial("FledgeSellerSignalsRequestsOneAtATime");
-
   // TODO(447306905) Enable testing for this field trial feature.
   // Reproducer:BiddingAndAuctionSerializerTest.SerializeWithTooSmallRequestSize
   disable_field_trial("FledgeEnableSampleDebugReportOnCookieSetting");
@@ -111,14 +95,6 @@
   // Reproducer:NavigationPolicyContainerBuilderTest.MHTMLSandboxFlags
   disable_field_trial("MHTML_Improvements");
 
-  // TODO(447306955) Enable testing for this field trial feature.
-  // Reproducer:SellerWorkletTest.ReportResultTextConversions
-  disable_field_trial("FledgeTextConversionHelpers");
-
-  // TODO(447306906) Enable testing for this field trial feature.
-  // Reproducer:BidderWorkletTest.GenerateBidInterestGroupCreativeScanningMetadata
-  disable_field_trial("FledgeTrustedSignalsKVv1CreativeScanning");
-
   // TODO(447306273) Enable testing for this field trial feature.
   // Reproducer:FileSystemAccessFileHandleImplRequestPermissionTest.RequestWrite_Denied
   disable_field_trial("TrackEmptyRendererProcessesForReuse");
diff --git a/content/test/data/accessibility/css/carousel-with-tabs-dynamic-expected-blink.txt b/content/test/data/accessibility/css/carousel-with-tabs-dynamic-expected-blink.txt
new file mode 100644
index 0000000..281df537
--- /dev/null
+++ b/content/test/data/accessibility/css/carousel-with-tabs-dynamic-expected-blink.txt
@@ -0,0 +1,14 @@
+rootWebArea name='Done'
+++genericContainer ignored
+++++genericContainer ignored
+++++++genericContainer
+++++++++tabPanel
+++++++++++link name='3'
+++++++++++++staticText name='3'
+++++++++++++++inlineTextBox name='3'
+++++++++++staticText name='Three'
+++++++++++++inlineTextBox name='Three'
+++++++tabList horizontal controlsIds=genericContainer
+++++++++tab selected=false detailsIds=null
+++++++++tab selected=false detailsIds=null
+++++++++tab selected=true detailsIds=tabPanel
diff --git a/content/test/data/accessibility/css/carousel-with-tabs-dynamic.html b/content/test/data/accessibility/css/carousel-with-tabs-dynamic.html
new file mode 100644
index 0000000..f14dc84f
--- /dev/null
+++ b/content/test/data/accessibility/css/carousel-with-tabs-dynamic.html
@@ -0,0 +1,63 @@
+<!--
+@WAIT-FOR:Done
+-->
+<!doctype html>
+
+<style>
+.carousel {
+  overflow: auto;
+  columns: 1;
+  height: 200px;
+  width: 200px;
+
+  scroll-marker-group: after tabs;
+
+  /* Scroll marker group is generated on the .carousel element */
+  &::scroll-marker-group {
+    height: 1rem;
+    width: 100%;
+    text-align: center;
+  }
+
+  /* These are div items children within the carousel */
+  & > div {
+    height: 100%;
+
+    /* Request a scroll marker for this element by setting non-none content */
+    &::scroll-marker {
+      display: inline-block;
+      content: '';
+      width: 1rem;
+      height: 1rem;
+      border: 1px solid black;
+    }
+    /* The currently selected scroll marker should be blue */
+    &::scroll-marker:target-current {
+      background: blue;
+    }
+  }
+}
+</style>
+
+<div id="scroller" class=carousel>
+  <div><a href="#">One</a></div>
+  <div>Two</div>
+  <div><span><a href="#">3</a></span>Three</div>
+</div>
+<script>
+  document.addEventListener("DOMContentLoaded", () => {
+    const a11ySyncTimeMs = 1000;
+    document.documentElement.offsetTop;
+    scroller.scrollLeft = 400;
+    document.documentElement.offsetTop;
+    setTimeout(() => {
+      requestAnimationFrame(() => {
+        requestAnimationFrame(() => {
+          document.documentElement.offsetTop;
+          // Signal the end of the test.
+          document.title = "Done";
+        });
+      });
+    }, a11ySyncTimeMs);
+  });
+</script>
diff --git a/content/test/io_thread_shared_url_loader_factory_owner.h b/content/test/io_thread_shared_url_loader_factory_owner.h
index 6dd3a65b..8466993 100644
--- a/content/test/io_thread_shared_url_loader_factory_owner.h
+++ b/content/test/io_thread_shared_url_loader_factory_owner.h
@@ -10,7 +10,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 
 class GURL;
 
diff --git a/content/test/web_contents_observer_consistency_checker.cc b/content/test/web_contents_observer_consistency_checker.cc
index 9f7962f..a24c597 100644
--- a/content/test/web_contents_observer_consistency_checker.cc
+++ b/content/test/web_contents_observer_consistency_checker.cc
@@ -353,15 +353,6 @@
   std::erase(active_media_players_, id);
 }
 
-bool WebContentsObserverConsistencyChecker::OnMessageReceived(
-    const IPC::Message& message,
-    RenderFrameHost* render_frame_host) {
-  CHECK(render_frame_host->IsRenderFrameLive());
-
-  AssertRenderFrameExists(render_frame_host);
-  return false;
-}
-
 void WebContentsObserverConsistencyChecker::WebContentsDestroyed() {
   CHECK(!web_contents_destroyed_);
   web_contents_destroyed_ = true;
diff --git a/content/test/web_contents_observer_consistency_checker.h b/content/test/web_contents_observer_consistency_checker.h
index 4cbf852..57d974f 100644
--- a/content/test/web_contents_observer_consistency_checker.h
+++ b/content/test/web_contents_observer_consistency_checker.h
@@ -79,8 +79,6 @@
       const MediaPlayerInfo& media_info,
       const MediaPlayerId& id,
       WebContentsObserver::MediaStoppedReason reason) override;
-  bool OnMessageReceived(const IPC::Message& message,
-                         RenderFrameHost* render_frame_host) override;
   void WebContentsDestroyed() override;
   void DidStartLoading() override;
   void DidStopLoading() override;
diff --git a/content/utility/services.cc b/content/utility/services.cc
index 4d5e498..f983285b 100644
--- a/content/utility/services.cc
+++ b/content/utility/services.cc
@@ -113,7 +113,8 @@
 #include "ui/accessibility/accessibility_features.h"
 #endif  // BUILDFLAG(ENABLE_ACCESSIBILITY_SERVICE)
 
-#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE)
+#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) || \
+    BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
 #include "media/capture/capture_switches.h"
 #include "services/viz/public/cpp/gpu/gpu.h"
 #include "services/viz/public/mojom/gpu.mojom.h"
@@ -375,8 +376,19 @@
 #if BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION)
 auto RunOOPVideoDecoderFactoryProcessService(
     mojo::PendingReceiver<media::mojom::VideoDecoderFactoryProcess> receiver) {
-  return std::make_unique<media::OOPVideoDecoderFactoryProcessService>(
+  auto service = std::make_unique<media::OOPVideoDecoderFactoryProcessService>(
       std::move(receiver));
+
+#if BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
+  mojo::PendingRemote<viz::mojom::Gpu> remote_gpu;
+  UtilityThread::Get()->BindHostReceiver(
+      remote_gpu.InitWithNewPipeAndPassReceiver());
+  std::unique_ptr<viz::Gpu> viz_gpu = viz::Gpu::Create(
+      std::move(remote_gpu), UtilityThread::Get()->GetIOTaskRunner());
+  service->SetVizGpu(std::move(viz_gpu));
+#endif  // BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
+
+  return service;
 }
 
 auto RunVideoEncodeAcceleratorProviderFactory(
@@ -402,10 +414,6 @@
   // loop of type IO that can get notified when pipes have data.
   services.Add(RunNetworkService);
 
-#if BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION)
-  services.Add(RunOOPVideoDecoderFactoryProcessService);
-#endif
-
   // Add new IO-thread services above this line.
   GetContentClient()->utility()->RegisterIOThreadServices(services);
 }
@@ -423,6 +431,10 @@
   services.Add(RunVideoEffects);
 #endif
 
+#if BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION)
+  services.Add(RunOOPVideoDecoderFactoryProcessService);
+#endif
+
   if (optimization_guide::features::CanLaunchOnDeviceModelService()) {
     services.Add(RunOnDeviceModel);
   }
diff --git a/content/web_test/browser/web_test_control_host.cc b/content/web_test/browser/web_test_control_host.cc
index 8e29df69..a37aa363 100644
--- a/content/web_test/browser/web_test_control_host.cc
+++ b/content/web_test/browser/web_test_control_host.cc
@@ -1323,7 +1323,7 @@
       ShellContentBrowserClient::Get()->browser_context();
 
   base::RepeatingClosure barrier_closure = base::BarrierClosure(
-      2, base::BindOnce(&WebTestControlHost::PrepareRendererForNextWebTest,
+      3, base::BindOnce(&WebTestControlHost::PrepareRendererForNextWebTest,
                         weak_factory_.GetWeakPtr()));
 
   StoragePartition* storage_partition =
@@ -1332,6 +1332,41 @@
       barrier_closure);
   storage_partition->ClearBluetoothAllowedDevicesMapForTesting();
 
+  // Clear all site-related storage APIs to ensure tests are hermetic.
+  // Use an "opt-out" (or "blacklist") approach for future-proofing. This
+  // ensures that new storage APIs added to `REMOVE_DATA_MASK_ALL` in the
+  // future are automatically cleared without needing to modify this code.
+  const uint32_t exclusion_mask =
+      // Cookies are excluded to preserve the state of the test runner itself
+      // and any test-specific setup.
+      content::StoragePartition::REMOVE_DATA_MASK_COOKIES |
+      // Media licenses can be costly to re-acquire and are not considered
+      // typical, per-test site data.
+      content::StoragePartition::REMOVE_DATA_MASK_MEDIA_LICENSES |
+      // Internal flags manage browser-internal state, not website data, and
+      // should not be cleared.
+      content::StoragePartition::
+          REMOVE_DATA_MASK_ATTRIBUTION_REPORTING_INTERNAL |
+      content::StoragePartition::REMOVE_DATA_MASK_PRIVATE_AGGREGATION_INTERNAL |
+      content::StoragePartition::REMOVE_DATA_MASK_INTEREST_GROUPS_INTERNAL |
+      // These flags are designed for explicit user actions in settings.
+      content::StoragePartition::REMOVE_DATA_MASK_INTEREST_GROUPS_USER_CLEAR |
+      // This is a transient network state, not persistent storage.
+      content::StoragePartition::REMOVE_KEEPALIVE_LOADS_ATTEMPTING_RETRY |
+      // Device-bound sessions are security/session-related and should persist.
+      content::StoragePartition::REMOVE_DATA_MASK_DEVICE_BOUND_SESSIONS;
+
+  const uint32_t removal_mask =
+      content::StoragePartition::REMOVE_DATA_MASK_ALL & ~exclusion_mask;
+
+  storage_partition->ClearData(
+      removal_mask, content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
+      /*filter_builder=*/nullptr,
+      content::StoragePartition::StorageKeyPolicyMatcherFunction(),
+      /*cookie_deletion_filter=*/nullptr,
+      /*perform_storage_cleanup=*/false, base::Time::Min(), base::Time::Max(),
+      barrier_closure);
+
   // TODO(nhiroki): Add a comment about the reason why we terminate all shared
   // workers here.
   TerminateAllSharedWorkers(ShellContentBrowserClient::Get()
diff --git a/device/bluetooth/floss/floss_dbus_manager.h b/device/bluetooth/floss/floss_dbus_manager.h
index 1c14f391..c9d4a237 100644
--- a/device/bluetooth/floss/floss_dbus_manager.h
+++ b/device/bluetooth/floss/floss_dbus_manager.h
@@ -34,7 +34,7 @@
 class FlossAdapterClient;
 class FlossAdvertiserClient;
 class FlossBatteryManagerClient;
-class FlossClientBundle;
+class FlossDBusManager;
 class FlossDBusManagerSetter;
 class FlossGattManagerClient;
 class FlossLEScanClient;
@@ -47,6 +47,67 @@
 class FlossAdminClient;
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
+// The bundle of all DBus clients used by FlossDBusManager. The bundle is used
+// to control the correct ordering of creation and deletion of clients before
+// shutting down the system bus.
+class DEVICE_BLUETOOTH_EXPORT FlossClientBundle {
+ public:
+  FlossClientBundle(const FlossClientBundle&) = delete;
+  FlossClientBundle& operator=(const FlossClientBundle&) = delete;
+
+  explicit FlossClientBundle(bool use_stubs);
+  ~FlossClientBundle();
+
+  FlossManagerClient* manager_client() { return manager_client_.get(); }
+
+  FlossAdapterClient* adapter_client() { return adapter_client_.get(); }
+
+  FlossGattManagerClient* gatt_manager_client() {
+    return gatt_manager_client_.get();
+  }
+
+  FlossSocketManager* socket_manager() { return socket_manager_.get(); }
+
+  FlossLEScanClient* lescan_client() { return lescan_client_.get(); }
+
+  FlossLoggingClient* logging_client() { return logging_client_.get(); }
+
+  FlossAdvertiserClient* advertiser_client() {
+    return advertiser_client_.get();
+  }
+#if BUILDFLAG(IS_CHROMEOS)
+  FlossAdminClient* admin_client() { return admin_client_.get(); }
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
+  FlossBatteryManagerClient* battery_manager_client() {
+    return battery_manager_client_.get();
+  }
+
+  FlossBluetoothTelephonyClient* bluetooth_telephony_client() {
+    return bluetooth_telephony_client_.get();
+  }
+
+ private:
+  friend FlossDBusManagerSetter;
+  friend FlossDBusManager;
+
+  void ResetAdapterClients();
+
+  bool use_stubs_;
+  std::unique_ptr<FlossManagerClient> manager_client_;
+  std::unique_ptr<FlossAdapterClient> adapter_client_;
+  std::unique_ptr<FlossGattManagerClient> gatt_manager_client_;
+  std::unique_ptr<FlossSocketManager> socket_manager_;
+  std::unique_ptr<FlossLEScanClient> lescan_client_;
+  std::unique_ptr<FlossLoggingClient> logging_client_;
+  std::unique_ptr<FlossAdvertiserClient> advertiser_client_;
+  std::unique_ptr<FlossBatteryManagerClient> battery_manager_client_;
+  std::unique_ptr<FlossBluetoothTelephonyClient> bluetooth_telephony_client_;
+#if BUILDFLAG(IS_CHROMEOS)
+  std::unique_ptr<FlossAdminClient> admin_client_;
+#endif  // BUILDFLAG(IS_CHROMEOS)
+};
+
 // FlossDBusManager manages the lifetimes of D-Bus connections and clients. It
 // ensures the proper ordering of shutdowns for the D-Bus thread, connections
 // and clients.
@@ -358,67 +419,6 @@
   scoped_refptr<dbus::Bus> system_bus_;
 };
 
-// The bundle of all DBus clients used by FlossDBusManager. The bundle is used
-// to control the correct ordering of creation and deletion of clients before
-// shutting down the system bus.
-class DEVICE_BLUETOOTH_EXPORT FlossClientBundle {
- public:
-  FlossClientBundle(const FlossClientBundle&) = delete;
-  FlossClientBundle& operator=(const FlossClientBundle&) = delete;
-
-  explicit FlossClientBundle(bool use_stubs);
-  ~FlossClientBundle();
-
-  FlossManagerClient* manager_client() { return manager_client_.get(); }
-
-  FlossAdapterClient* adapter_client() { return adapter_client_.get(); }
-
-  FlossGattManagerClient* gatt_manager_client() {
-    return gatt_manager_client_.get();
-  }
-
-  FlossSocketManager* socket_manager() { return socket_manager_.get(); }
-
-  FlossLEScanClient* lescan_client() { return lescan_client_.get(); }
-
-  FlossLoggingClient* logging_client() { return logging_client_.get(); }
-
-  FlossAdvertiserClient* advertiser_client() {
-    return advertiser_client_.get();
-  }
-#if BUILDFLAG(IS_CHROMEOS)
-  FlossAdminClient* admin_client() { return admin_client_.get(); }
-#endif  // BUILDFLAG(IS_CHROMEOS)
-
-  FlossBatteryManagerClient* battery_manager_client() {
-    return battery_manager_client_.get();
-  }
-
-  FlossBluetoothTelephonyClient* bluetooth_telephony_client() {
-    return bluetooth_telephony_client_.get();
-  }
-
- private:
-  friend FlossDBusManagerSetter;
-  friend FlossDBusManager;
-
-  void ResetAdapterClients();
-
-  bool use_stubs_;
-  std::unique_ptr<FlossManagerClient> manager_client_;
-  std::unique_ptr<FlossAdapterClient> adapter_client_;
-  std::unique_ptr<FlossGattManagerClient> gatt_manager_client_;
-  std::unique_ptr<FlossSocketManager> socket_manager_;
-  std::unique_ptr<FlossLEScanClient> lescan_client_;
-  std::unique_ptr<FlossLoggingClient> logging_client_;
-  std::unique_ptr<FlossAdvertiserClient> advertiser_client_;
-  std::unique_ptr<FlossBatteryManagerClient> battery_manager_client_;
-  std::unique_ptr<FlossBluetoothTelephonyClient> bluetooth_telephony_client_;
-#if BUILDFLAG(IS_CHROMEOS)
-  std::unique_ptr<FlossAdminClient> admin_client_;
-#endif  // BUILDFLAG(IS_CHROMEOS)
-};
-
 }  // namespace floss
 
 #endif  // DEVICE_BLUETOOTH_FLOSS_FLOSS_DBUS_MANAGER_H_
diff --git a/device/fido/features.cc b/device/fido/features.cc
index 8fd1e5f3..1cbbdfc 100644
--- a/device/fido/features.cc
+++ b/device/fido/features.cc
@@ -102,6 +102,13 @@
              "WebAuthenticationHelloSignal",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+#if BUILDFLAG(IS_ANDROID)
+// Enabled by default in M142 Remove in or after M145.
+BASE_FEATURE(kWebAuthnAndroidSignal,
+             "WebAuthenticationAndroidSignal",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+#endif  // BUILDFLAG(IS_ANDROID)
+
 // Disabled by default.
 BASE_FEATURE(kDigitalCredentialsHybridLinking,
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/device/fido/features.h b/device/fido/features.h
index 4e916fe6..c867934 100644
--- a/device/fido/features.h
+++ b/device/fido/features.h
@@ -63,6 +63,12 @@
 COMPONENT_EXPORT(DEVICE_FIDO)
 BASE_DECLARE_FEATURE(kWebAuthnHelloSignal);
 
+#if BUILDFLAG(IS_ANDROID)
+// Enables the WebAuthn Signal API for Chrome on Android.
+COMPONENT_EXPORT(DEVICE_FIDO)
+BASE_DECLARE_FEATURE(kWebAuthnAndroidSignal);
+#endif  // BUILDFLAG(IS_ANDROID)
+
 // When enabled, skips configuring hybrid when Windows can do hybrid. Hybrid may
 // still be delegated to Windows regardless of this flag.
 COMPONENT_EXPORT(DEVICE_FIDO)
diff --git a/device/gamepad/public/cpp/gamepad_features.cc b/device/gamepad/public/cpp/gamepad_features.cc
index d5c8b03..bb48e761 100644
--- a/device/gamepad/public/cpp/gamepad_features.cc
+++ b/device/gamepad/public/cpp/gamepad_features.cc
@@ -30,9 +30,7 @@
              base::FEATURE_DISABLED_BY_DEFAULT);
 
 // Enables gamepad raw input change events.
-BASE_FEATURE(kGamepadRawInputChangeEvent,
-             "GamepadRawInputChangeEvent",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kGamepadRawInputChangeEvent, base::FEATURE_DISABLED_BY_DEFAULT);
 
 bool IsGamepadMultitouchEnabled() {
   if (base::FeatureList::IsEnabled(kEnableGamepadMultitouch)) {
diff --git a/docs/gerrit_reauth.md b/docs/gerrit_reauth.md
index 4cf0022d..5d9db0b7 100644
--- a/docs/gerrit_reauth.md
+++ b/docs/gerrit_reauth.md
@@ -55,9 +55,13 @@
 [ReAuth in git-cl remotely](#ReAuth-in-git_cl-remotely) to setup your
 environment.
 
-If you use Linux, you might need to
-[configure your system](#linux-security-keys-access) to make security keys
-usable.
+If you use Linux:
+
+1. You need to install a GUI-based `pinentry` program to enter security key
+   PINs. Certain security keys models mandate PIN entry at all times.
+
+1. You might also need to [configure your system](#linux-security-keys-access)
+   to make security keys usable.
 ***
 
 ## Prerequisites
@@ -80,6 +84,11 @@
 **Important Note**: Passkeys won't be supported by ReAuth. A physical security
 key is required.
 
+**If you use Firefox**: You need to allow the website to collect extended
+information about your security key in this dialog. Otherwise the key won't be
+able to ReAuth (you'll see BAD_REQUEST error in the log). If you've already
+registered the key, remove it from the security key list, then re-adding it.
+
 If you’re using a Google Workspace account, make sure
 "[2-Step Verification](https://myaccount.google.com/signinoptions/twosv)" is
 turned on.
@@ -156,6 +165,40 @@
 [Yubico’s guide](https://support.yubico.com/hc/en-us/articles/360013708900-Troubleshooting-using-your-YubiKey-with-Linux)
 , which we confirmed to be working on Ubuntu 24.04 LTS Desktop.
 
+### Linux: security key PIN entry program
+
+ReAuth doesn't require security key PINs. But PINs entry might be enforced by
+the security key manufacturer, or if you have configured your key to do so.
+
+On Linux, you need the `pinentry` program to input PINs. If you don't have this
+program, your security key will refuse to complete the ReAuth challenge. You
+typically see `BAD_REQUEST` or `PinRequiredError` in the logs depending on the
+security key.
+
+For the best experience, we recommend using a **GUI based pinentry** program.
+
+Terminal based pinentry only works with local ReAuth. If you don't need to
+ReAuth over SSH, feel free to use one.
+
+To install a GUI-based pinentry program:
+
+* Ubuntu, Debian: `sudo apt install pinentry-gnome3`
+* Fedora: `sudo dnf install pinentry-qt`
+
+After installing the package, your system should default to the newly installed
+GUI-based pinentry program.
+
+You can check the current pinentry program by running:
+
+```
+readlink -f $( which pinentry )
+```
+
+The output path's suffix should be a GUI based name, such as "-gnome" or "-qt".
+
+If the above path ends with terminal based name, such as "tty" or "curses", set
+`LUCI_AUTH_PINENTRY=pinentry-gnome3` environment variable to override.
+
 ## ReAuth in Gerrit Web UI
 
 When performing actions such as voting Code-Review or editing commit
diff --git a/docs/security/faq.md b/docs/security/faq.md
index 54f13857..33c3e31f 100644
--- a/docs/security/faq.md
+++ b/docs/security/faq.md
@@ -388,6 +388,19 @@
 are always out of scope, but it does mean that spoofs which would not deceive
 a user being reasonable and prudent are out of scope.
 
+<a name="TOC-As-a-user_I-can-bypass-an-enterprise-policy-is-this-a-security-bug_"></a>
+### As a user, I can bypass an enterprise policy - is this a security bug?
+In general, no. Enterprise policies applying to running, enterprise-enrolled
+Chrome instances are not by default a security boundary. It may be a functional
+bug in the implementation of the enterprise policy, or it may be intended
+behavior, but either way actions by the local user are generally considered to
+be "local attacks" and outside our threat model.
+
+ChromeOS is an exception to this. On ChromeOS, Chrome integrates more deeply
+with the host operating system and is able to provide stronger guarantees about
+policies.  Therefore, an enterprise policy bypass by a local user of a ChromeOS
+device may still be a security bug.
+
 ## Areas outside Chrome's Threat Model
 
 <a name="TOC-Are-privacy-issues-considered-security-bugs-"></a>
diff --git a/docs/site_search_explainer.md b/docs/site_search_explainer.md
new file mode 100644
index 0000000..d567d85
--- /dev/null
+++ b/docs/site_search_explainer.md
@@ -0,0 +1,382 @@
+# Site Search in Omnibox Explainer
+
+This document provides a technical overview of the Omnibox Scoped Search (also referred to as "site
+search" or "keyword search") functionality for Chrome engineers. The architecture and code paths
+described are shared across Windows, Mac, Linux, and ChromeOS. It is based on a detailed code
+analysis as of this writing (2025-09-24) covers two primary scenarios: scoped search via traditional
+keywords (e.g. youtube.com) and scoped search via built-in keywords (also known as "starter pack"
+keywords), which use the "@" syntax (e.g., @history).
+
+## Types of Keywords
+
+Keywords in the Omnibox come from various sources and have different behaviors. Here's a breakdown
+of the most common types:
+
+- **Starter Packs**: Built-in keywords with the "@" prefix (e.g., `@bookmarks`, `@history`).
+- **Prepopulated**: Keywords for common search engines (e.g., `bing.com`, `yahoo.com`).
+- **User Created (Traditional)**: A user-created keyword where the template URL contains a %s
+  placeholder (e.g., `youtube` search).
+- **Non-substituting User Created**: A user-created keyword where the template URL does not contain
+  a %s placeholder.
+- **Default Search Engine (DSE)**: The user's currently selected default search engine, which is
+  kind of orthogonal to the other types here.
+- **Extension**: Keywords registered by browser extensions (e.g., an `ssh` keyword registered by the
+  Secure Shell extension).
+- **Enterprise Policy**: Keywords set by a device administrator through enterprise policies. These
+  can be further subdivided into:
+  - **Aggregator**: e.g.,`@agentspace`.
+  - **Non-aggregator**: e.g., `microsoft-documents`.
+- **Admin Policy**: Keywords set by an admin policy.
+
+## High-Level Overview
+
+The Omnibox uses a sophisticated system of providers, managed by the
+[`AutocompleteController`](https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/autocomplete_controller.h),
+to generate suggestions. Site Search functionality is primarily handled by two distinct providers:
+
+1. **[`FeaturedSearchProvider`](https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/featured_search_provider.h)**:
+   Manages the "Omnibox Starter Pack," which provides scoped search suggestions like `@history`,
+   `@bookmarks`, and `@tabs`, as well as enterprise keywords (e.g. @agentspace or
+   facebook-internal-documents).
+2. **[`KeywordProvider`](https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/keyword_provider.h)**:
+   Manages searches directed at specific, non-default search engines using keywords. This is powered
+   by the
+   [`TemplateURLService`](https://source.chromium.org/chromium/chromium/src/+/main:components/search_engines/template_url_service.h),
+   which stores URL templates for various sites.
+
+While `KeywordProvider` and `FeaturedSearchProvider` manage surfacing the keyword entry points, most
+providers (e.g., tabs, bookmarks, history, search, ...) are responsible for populating the
+suggestions when in keyword mode. e.g.
+
+- 'youtube.com query<enter>' will be handled by search provider.
+- 'youtube.com cat video<down><down><enter>' to select a past youtube navigation will be handled by
+  a history provider.
+
+These providers rely on the `TemplateURLService`, which is populated with
+[`TemplateURL`](https://source.chromium.org/chromium/chromium/src/+/main:components/search_engines/template_url.h)
+objects. These objects can come from several sources:
+
+- For enterprise and policy keywords, they're set by policy.
+- For pre-populated keywords, they're hardcoded into the binary, but in a json or some sort of data
+  file, not necessarily in .cc directly.
+- For extensions, they come from the extensions.
+
+The general flow is as follows:
+
+1. User types into the Omnibox.
+2. `AutocompleteController` starts and queries all registered providers with the user's input.
+3. `FeaturedSearchProvider` and `KeywordProvider`, among others, generate
+   [`AutocompleteMatch`](https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/autocomplete_match.h)
+   objects based on the input.
+4. These matches are ranked and displayed to the user.
+5. When a user selects a match that happens to have a keyword chip, they will navigate to the match,
+   ignoring the keyword chip. When a user selects the keyword chip on the match, they will enter
+   keyword mode (to handle subsequent input) which may or may not ultimately navigate to the target
+   site. (You can enter youtube keyword mode by typing 'youtube.com query' then select a
+   drive.google.com document titled 'youtube.com query' that does not navigate to youtube.com.)
+
+## Key UI Component Roles
+
+The `LocationBarView` is the top level component of omnibox view hierarchy. See
+[Overview of the OmniboxView Hierarchy](https://docs.google.com/document/d/1CPAEWojYkGxRL6j3REnWtVFbDz8A7vtFXf9RXmmWyEY)
+(internal document) for a picture.
+
+- **[`LocationBarView`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/location_bar/location_bar_view.h)**
+  owns the omnibox view, the popup view, and selected keyword view. It is mostly responsible for
+  updating the keyword chip in the input row (e.g., "Search History").
+
+- **[`OmniboxView`](https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/omnibox_view.h)**:
+  This is the text field where the user types. Its implementation (e.g., `OmniboxViewViews`) is
+  responsible for rendering the user's text, and handling input. It communicates user actions, like
+  typing, to the `OmniboxEditModel`.
+
+- **[`OmniboxPopupView`](https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/omnibox_popup_view.h)**:
+  This is the dropdown list of suggestions that appears below the text field. Its implementation
+  (e.g., `OmniboxPopupViewViews`) is responsible for rendering each suggestion line (using
+  `OmniboxResultView`). It receives the list of `AutocompleteMatch` objects from the model and
+  informs the model when the user interacts with the list (e.g., hovering or selecting a
+  suggestion).
+
+The `OmniboxEditModel` acts as a controller for these views. For example, when a user selects a
+suggestion in the `OmniboxPopupView`, the popup notifies the model. The model then updates its
+internal state and may, in turn, commands the `OmniboxView` to change its displayed text or show a
+keyword chip.
+
+While the view and model are mutually aware of each other to facilitate this communication, their
+ownership is hierarchical to prevent circular dependencies. The `OmniboxView` is owned by its parent
+UI component (e.g., `LocationBarView`). The view, in turn, owns the
+[`OmniboxController`](https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/omnibox_controller.h),
+which owns the `OmniboxEditModel`. This creates a clear ownership chain: `OmniboxView` →
+`OmniboxController` → `OmniboxEditModel`. Communication back up the chain is handled via non-owning
+`raw_ptr` references.
+
+## Scenario 1: Scoped Search (`@history`)
+
+This scenario describes how a user can scope their search to their local browser history.
+
+**User Journey:**
+
+1. User types `@hist`.
+2. An autocomplete suggestion for "Search History" appears.
+3. User selects this suggestion.
+4. The text `@history` disappears from the input field and is replaced by a "Search History" chip.
+5. User types a query, e.g., `foo`, and presses Enter.
+6. The browser navigates to `chrome://history/?q=foo`.
+
+### Technical Deep Dive
+
+1. **Suggestion Generation (`@hist`)**
+
+   - **Component**: `FeaturedSearchProvider`, `TemplateURLService`
+   - **Mechanism**: The `@` prefixed suggestions are part of the "Omnibox Starter Pack" feature,
+     which are managed as special `TemplateURL` objects.
+     - The `TemplateURLService` is loaded with a set of default "starter pack" engines, including
+       one for History with the keyword `@history` and a unique `starter_pack_id`. This data is
+       loaded from `components/search_engines/template_url_starter_pack_data.cc`.
+     - `FeaturedSearchProvider::Start()` is called on input. It detects that the input starts with
+       `@` and queries the `TemplateURLService` for matching keywords.
+     - It finds the `@history` `TemplateURL` object.
+     - `FeaturedSearchProvider` then creates an `AutocompleteMatch` with the type
+       `AutocompleteMatch::Type::STARTER_PACK`.
+
+2. **Mode Activation (Selecting the suggestion)**
+
+   - **Component**:
+     [`OmniboxEditModel`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/omnibox/omnibox_edit_model.h),
+     `AutocompleteController`
+   - **Mechanism**: When the user selects the `STARTER_PACK` match, `OmniboxEditModel::OpenMatch()`
+     is called. This activates "keyword mode" with `@history` as the keyword.
+     - The `AutocompleteController` is restarted with the new input. It recognizes that the omnibox
+       is in keyword mode with a `TemplateURL` that has a `starter_pack_id` for history.
+     - Crucially, the controller then adjusts its logic to only run a limited set of providers
+       relevant to history search:
+       [`HistoryURLProvider`](https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/history_url_provider.h),
+       [`HistoryEmbeddingsProvider`](https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/history_embeddings_provider.h),
+       and `FeaturedSearchProvider`.
+
+3. **UI State Management (The Chip and Text Replacement)**
+
+   - **Component**: `OmniboxEditModel`,
+     [`OmniboxView`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/omnibox/omnibox_view.h)
+   - **Mechanism**: The separation between the model's state and the view's displayed text is key.
+     - The `OmniboxEditModel` sets its internal state to keyword mode, storing `@history`
+       internally.
+     - It then clears the user's visible text, replacing it with an empty string.
+     - The model notifies the `OmniboxView`, which sees that the model is in keyword mode
+       (`is_keyword_selected()` is true) and renders the "Search History" chip instead of the
+       keyword text.
+     - Specifically, the
+       [`SelectedKeywordView`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/location_bar/selected_keyword_view.h)
+       is shown. It retrieves the `TemplateURL` for `@history`, gets its short name ("History"), and
+       uses this to format a localized string (e.g., "Search History").
+
+4. **Final Navigation (`foo` + Enter)**
+   - **Component**: `HistoryURLProvider`, `AutocompleteController`
+   - **Mechanism**: As the user types `foo`, the `AutocompleteController` runs the filtered set of
+     providers (e.g., `HistoryURLProvider`) against the input `foo`.
+     - `HistoryURLProvider` finds matching history entries for "foo".
+     - It creates `AutocompleteMatch` objects with `destination_url`s pointing to the relevant
+       history pages (e.g., `chrome://history/?q=foo`).
+     - When the user presses Enter, the browser navigates to the selected history result.
+
+## Scenario 2: Keyword Search (`youtube.com`)
+
+This scenario describes how a user can search a specific website directly from the Omnibox using a
+keyword.
+
+**User Journey:**
+
+1. User performs a search on `youtube.com`.
+2. Chrome automatically generates an inactive `TemplateURL` for YouTube search by detecting the
+   site's OpenSearch description.
+3. The user navigates to `chrome://settings/searchEngines`.
+4. The YouTube entry appears in the "inactive" list of site search engines.
+5. The user explicitly activates the YouTube entry.
+6. Now, when the user types "you" in the Omnibox, a suggestion for `youtube.com` appears with a
+   "Search YouTube" hint, and site search is fully enabled.
+
+### Technical Deep Dive
+
+1. **Search Engine Discovery and Storage**
+
+   - **Component**: `TemplateURLService`
+   - **Mechanism**: When a user submits a search form on a website that provides an
+     [OpenSearch description document](<https://en.wikipedia.org/wiki/OpenSearch_(specification)>),
+     Chrome's renderer process can detect it and send an IPC message to the browser process. The
+     `TemplateURLService` then creates and stores a `TemplateURL` object. This object contains the
+     site's name, the keyword (e.g., `youtube.com`), and the URL template for performing a search
+     (e.g., `https://www.youtube.com/results?search_query={searchTerms}`).
+
+2. **Automatic Keyword Generation**
+
+   - **Component**:
+     [`SearchEngineTabHelper`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/search_engines/search_engine_tab_helper.h),
+     `TemplateURLService`
+   - **Mechanism**: When a user performs a search on a website, Chrome can automatically generate a
+     keyword for that site.
+     - `SearchEngineTabHelper::GenerateKeywordIfNecessary()` is called on navigation.
+     - It checks if the navigation is a form submission and if a keyword can be generated from the
+       URL.
+     - If so, it calls `TemplateURLService::CanAddAutogeneratedKeyword()` to check if a new keyword
+       can be added. This is to avoid adding duplicate or conflicting keywords.
+     - If a new keyword can be added, a
+       [`TemplateURLData`](https://source.chromium.org/chromium/chromium/src/+/main:components/search_engines/template_url_data.h)
+       object is created and added to the `TemplateURLService`.
+     - This newly created `TemplateURL` is marked as `safe_for_autoreplace`, meaning it can be
+       replaced by a more official keyword later (e.g., one from an OpenSearch description
+       document).
+     - The `is_active` status is set to `kUnspecified`, so the keyword is not immediately usable.
+
+3. **Activation**
+
+   - **Component**: `TemplateURLService`
+   - **Mechanism**: A newly created `TemplateURL` is not immediately active (`is_active` is
+     `kUnspecified`). For it to be used for keyword search, it must be activated. There are two
+     paths to activation:
+     - **Manual Activation**: This is the immediate path. When a user goes to
+       `chrome://settings/searchEngines` and activates an entry,
+       `TemplateURLService::SetIsActiveTemplateURL()` is called. This function explicitly sets
+       `is_active` to `kTrue` and also sets `safe_for_autoreplace` to `false`, ensuring the user's
+       choice is preserved. This change takes effect immediately in the current session.
+     - **Automatic Generation**: As described in the previous section, keywords can be generated
+       automatically. However, they must be manually activated by the user in
+       `chrome://settings/searchEngines`.
+
+4. **Suggestion Generation (`youtube.com foo`)**
+
+   - **Component**: `KeywordProvider`
+   - **Mechanism**: `KeywordProvider::Start()` is called with the user's input.
+     - It calls `AutocompleteInput::ExtractKeywordFromInput()` to parse the input into a potential
+       keyword (`youtube.com`) and the remaining query (`foo`).
+     - It then calls `TemplateURLService::AddMatchingKeywords()` with the extracted keyword.
+     - `TemplateURLService` finds the active `TemplateURL` object matching `youtube.com`.
+     - `KeywordProvider` receives this `TemplateURL` and proceeds to call its internal
+       `CreateAutocompleteMatch()` method. This creates an `AutocompleteMatch` of type
+       `AutocompleteMatch::Type::SEARCH_KEYWORD`.
+
+5. **Final Navigation (Enter)**
+   - **Component**: `KeywordProvider`
+   - **Mechanism**: The final URL is constructed within `KeywordProvider::FillInUrlAndContents()`.
+     - This function is called by `CreateAutocompleteMatch()`.
+     - It uses the `TemplateURLRef` from the `TemplateURL` object. The `ReplaceSearchTerms()` method
+       is called on this reference.
+     - This method substitutes the `{searchTerms}` placeholder in the URL template with the user's
+       query (`foo`).
+     - The resulting URL (e.g., `https://www.youtube.com/results?search_query=foo`) is set as the
+       `destination_url` of the `AutocompleteMatch`. When the user presses Enter, the browser
+       navigates to this URL.
+
+## Keyword State in Core Data Structures
+
+To fully understand the logic, it's crucial to see how keyword state is tracked within the core data
+structures: `AutocompleteInput` and `AutocompleteMatch`.
+
+### `AutocompleteInput`
+
+The `AutocompleteInput` object represents the user's query and the omnibox's state. It has two key
+fields, `prefer_keyword_` and `allow_exact_keyword_match_`, which are used to signal that when the
+user is in keyword mode, providers should show more "keyword-y" suggestions. For example, a search
+for 'query' should suggest 'youtube.com/q=query' instead of 'google.com/q=query' when the user is in
+the YouTube keyword mode.
+
+### `AutocompleteMatch`
+
+The `AutocompleteMatch` object represents a single suggestion and contains several nuanced fields
+for managing keywords:
+
+- **`associated_keyword`**: This field determines which keyword to activate via the keyword chip.
+  For example, the `@history` keyword activates the `chrome://history`... template URL. This can be
+  a source of confusion, because when a user types "youtube", an autocomplete match might appear
+  that navigates to 'amazon.com/youtube' but also has a keyword chip to activate the `youtube.com`
+  keyword. In this case, `associated_keyword` will store 'youtube.com', even though the rest of the
+  match fields are Amazon-related.
+
+- **`keyword`**: This field tracks which `TemplateURL` was used to create the match's destination
+  URL. For example, if the user types 'youtube.com query', the `keyword` field will track 'youtube'.
+  This can also be confusing, because even when the user isn't in keyword mode, most search
+  suggestions are still generated using a keyword and will have their `keyword` field set (e.g., to
+  `google.com` for the default search engine).
+
+- **`from_keyword`**: This field tracks if the match was generated while the user is in keyword
+  mode. This is another potential point of confusion, as it can be `true` even if the match doesn't
+  have a `keyword` field set. Conversely, `from_keyword` can be `false` even when `keyword` is set.
+  In essence, `from_keyword` reflects the _input state_ when the match was created, while `keyword`
+  reflects the match's _data source_.
+
+## Methods for Model-View Interaction
+
+A key aspect of the Omnibox architecture is the interaction between the `OmniboxEditModel` (the
+model) and its two main views. The model holds the state, while the views are responsible for
+rendering the UI. The views are updated via a tightly-coupled observer-like pattern, where the model
+directly calls methods on the views when its state changes.
+
+The following table summarizes the key methods that facilitate this interaction within the scope of
+the scenarios described in this document.
+
+| Initiator (Caller)       | Target (Callee)    | Method Called                         | Purpose                                                                          |
+| :----------------------- | :----------------- | :------------------------------------ | :------------------------------------------------------------------------------- |
+| `OmniboxView`            | `OmniboxEditModel` | `OnAfterPossibleChange()`             | Notifies the model of user text input.                                           |
+| `OmniboxEditModel`       | `OmniboxView`      | `SetWindowTextAndCaretPos()`          | Instructs the text view to change its content, e.g., when entering keyword mode. |
+| `OmniboxPopupView`       | `OmniboxEditModel` | `OpenMatch()` / `SetPopupSelection()` | Notifies the model that the user has selected or highlighted a suggestion.       |
+| `OmniboxEditModel`       | `OmniboxPopupView` | `UpdatePopupAppearance()`             | Instructs the popup to redraw itself based on the model's current state.         |
+| `AutocompleteController` | `OmniboxEditModel` | `OnResultChanged()`                   | Notifies the model that the autocomplete results have changed.                   |
+
+---
+
+## Appendix: Architecture on Chrome for Android
+
+The architecture described above is specific to desktop platforms which use the C++ Views UI
+framework. Chrome for Android has a distinct architecture where the UI is primarily implemented in
+Java and built upon a `Coordinator` pattern. This pattern distributes responsibilities among several
+specialized components, in contrast to the more monolithic `OmniboxEditModel` on desktop.
+
+However, the core suggestion generation logic is shared. The C++ `AutocompleteController` is used by
+all platforms to generate suggestion results.
+
+### The Coordinator Pattern on Android
+
+On Android, the Omnibox (known as the Location Bar) is managed by a top-level
+[`LocationBarCoordinator`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java).
+This coordinator owns and orchestrates a set of sub-components, each with a specific responsibility.
+The responsibilities of the desktop `OmniboxEditModel` are split among these components:
+
+- **`LocationBarCoordinator`**: The overall owner and assembler. It creates the other coordinators
+  and mediators and wires them together.
+
+- **[`AutocompleteCoordinator`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java)**:
+  Manages the suggestion list UI, including the
+  [`OmniboxSuggestionsDropdown`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java)
+  view. It creates and owns the `AutocompleteMediator`.
+
+- **[`AutocompleteMediator`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java)**:
+  The primary logic hub for autocomplete. It is responsible for communicating with the C++
+  `AutocompleteController` via JNI, processing the results, and updating the `PropertyModel` that
+  drives the suggestion list UI.
+
+- **[`UrlBarCoordinator`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java)**:
+  Manages the
+  [`UrlBar`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java)
+  view, which is the `EditText` field the user types into. It handles text state, cursor position,
+  and selection, reporting user input to other components.
+
+- **[`LocationBarMediator`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java)**:
+  A central logic hub that connects the other components. It handles events like focus changes and
+  button clicks, and manages the overall state of the location bar that doesn't directly involve
+  text editing or autocomplete suggestions.
+
+### Interaction Flow for Suggestions
+
+1. A user types into the `UrlBar` view (managed by `UrlBarCoordinator`).
+2. During initialization, `LocationBarCoordinator` sets up a listener so that text changes in
+   `UrlBarCoordinator` directly call `AutocompleteCoordinator.onTextChanged()`.
+3. `AutocompleteCoordinator.onTextChanged()` forwards the call to
+   `AutocompleteMediator.onTextChanged()`.
+4. The `AutocompleteMediator` takes the user's input and initiates a request to the shared C++
+   `AutocompleteController` through the JNI bridge.
+5. When results are ready, the C++ `AutocompleteController` sends them back to the
+   `AutocompleteMediator` via its `onSuggestionsReceived` JNI callback.
+6. The `AutocompleteMediator` receives the `AutocompleteResult`, processes it, builds a list of view
+   models, and updates the main `PropertyModel` for the suggestion list.
+7. The `PropertyModel` change is observed by the
+   [`OmniboxSuggestionsDropdownAdapter`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownAdapter.java),
+   which then renders the final views in the suggestion dropdown list.
diff --git a/extensions/browser/service_worker_manager.cc b/extensions/browser/service_worker_manager.cc
index d229678..5cc9c33 100644
--- a/extensions/browser/service_worker_manager.cc
+++ b/extensions/browser/service_worker_manager.cc
@@ -9,10 +9,31 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/service_worker_context.h"
 #include "extensions/browser/extension_util.h"
+#include "extensions/browser/extensions_browser_client.h"
+#include "extensions/common/manifest_handlers/incognito_info.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 
 namespace extensions {
 
+namespace {
+
+// Returns the incognito browser context if it exists and the given `extension`
+// is enabled in incognito and is in split mode. Otherwise, returns nullptr.
+content::BrowserContext* GetIncognitoContextForSplitMode(
+    content::BrowserContext* context,
+    const Extension* extension) {
+  if (context->IsOffTheRecord() ||
+      !ExtensionsBrowserClient::Get()->HasOffTheRecordContext(context) ||
+      !IncognitoInfo::IsSplitMode(extension) ||
+      !ExtensionsBrowserClient::Get()->IsExtensionIncognitoEnabled(
+          extension->id(), context)) {
+    return nullptr;
+  }
+  return ExtensionsBrowserClient::Get()->GetOffTheRecordContext(context);
+}
+
+}  // namespace
+
 ServiceWorkerManager::ServiceWorkerManager(
     content::BrowserContext* browser_context)
     : browser_context_(browser_context) {
@@ -28,6 +49,16 @@
   util::GetServiceWorkerContextForExtensionId(extension->id(), browser_context_)
       ->StopAllServiceWorkersForStorageKey(
           blink::StorageKey::CreateFirstParty(extension->origin()));
+
+  // If the extension is in split mode, we need to also stop the service
+  // workers in the incognito context.
+  if (content::BrowserContext* incognito_context =
+          GetIncognitoContextForSplitMode(browser_context_, extension)) {
+    util::GetServiceWorkerContextForExtensionId(extension->id(),
+                                                incognito_context)
+        ->StopAllServiceWorkersForStorageKey(
+            blink::StorageKey::CreateFirstParty(extension->origin()));
+  }
 }
 
 void ServiceWorkerManager::OnExtensionUninstalled(
@@ -42,6 +73,15 @@
       ->DeleteForStorageKey(
           blink::StorageKey::CreateFirstParty(extension->origin()),
           base::DoNothing());
+
+  if (content::BrowserContext* incognito_context =
+          GetIncognitoContextForSplitMode(browser_context_, extension)) {
+    util::GetServiceWorkerContextForExtensionId(extension->id(),
+                                                incognito_context)
+        ->DeleteForStorageKey(
+            blink::StorageKey::CreateFirstParty(extension->origin()),
+            base::DoNothing());
+  }
 }
 
 }  // namespace extensions
diff --git a/extensions/browser/task_queue_util.cc b/extensions/browser/task_queue_util.cc
index b7d885e..7117d48 100644
--- a/extensions/browser/task_queue_util.cc
+++ b/extensions/browser/task_queue_util.cc
@@ -103,7 +103,8 @@
 
   // There is a separate task queue for the off-the-record context
   // for any extension running in split mode.
-  if (!ExtensionsBrowserClient::Get()->HasOffTheRecordContext(
+  if (browser_context->IsOffTheRecord() ||
+      !ExtensionsBrowserClient::Get()->HasOffTheRecordContext(
           browser_context) ||
       !IncognitoInfo::IsSplitMode(extension) ||
       !ExtensionsBrowserClient::Get()->IsExtensionIncognitoEnabled(
diff --git a/extensions/common/api/declarative_net_request.idl b/extensions/common/api/declarative_net_request.idl
index 668d9626..4f3f780a7 100644
--- a/extensions/common/api/declarative_net_request.idl
+++ b/extensions/common/api/declarative_net_request.idl
@@ -52,8 +52,9 @@
 
   // This describes the possible operations for a "modifyHeaders" rule.
   enum HeaderOperation {
-    // Adds a new entry for the specified header. This operation is not
-    // supported for request headers.
+    // Adds a new entry for the specified header. When modifying the headers of
+    // a request, this operation is only supported for
+    // <a href="#header_modification">specific headers</a>.
     append,
     // Sets a new value for the specified header, removing any existing headers
     // with the same name.
diff --git a/extensions/common/extension_features.cc b/extensions/common/extension_features.cc
index fa2189d..bf17a4e 100644
--- a/extensions/common/extension_features.cc
+++ b/extensions/common/extension_features.cc
@@ -155,7 +155,6 @@
              base::FEATURE_DISABLED_BY_DEFAULT);
 
 BASE_FEATURE(kExtensionBrowserNamespaceAlternative,
-             "ExtensionBrowserNamespaceAlternative",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
 BASE_FEATURE(kOptimizeServiceWorkerStartRequests,
@@ -168,7 +167,6 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kRuntimeOnMessageWebExtensionPolyfillSupport,
-             "RuntimeOnMessageWebExtensionPolyfillSupport",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
 }  // namespace extensions_features
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index ce10b00..1ce0748b 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -223,8 +223,6 @@
     "sampler_manager.h",
     "scheduler_sequence.cc",
     "scheduler_sequence.h",
-    "service_discardable_manager.cc",
-    "service_discardable_manager.h",
     "service_font_manager.cc",
     "service_font_manager.h",
     "service_transfer_cache.cc",
diff --git a/gpu/command_buffer/service/command_buffer_task_executor.cc b/gpu/command_buffer/service/command_buffer_task_executor.cc
index 14953d5..49e8a1dc 100644
--- a/gpu/command_buffer/service/command_buffer_task_executor.cc
+++ b/gpu/command_buffer/service/command_buffer_task_executor.cc
@@ -7,6 +7,7 @@
 #include "gpu/command_buffer/service/gpu_tracer.h"
 #include "gpu/command_buffer/service/memory_program_cache.h"
 #include "gpu/command_buffer/service/program_cache.h"
+#include "gpu/config/gpu_driver_bug_workaround_type.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_share_group.h"
 
@@ -24,7 +25,6 @@
       sync_point_manager_(sync_point_manager),
       share_group_surface_format_(share_group_surface_format),
       program_cache_(program_cache),
-      discardable_manager_(gpu_preferences_),
       shader_translator_cache_(gpu_preferences_),
       shared_image_manager_(shared_image_manager) {
   DCHECK(shared_image_manager_);
diff --git a/gpu/command_buffer/service/command_buffer_task_executor.h b/gpu/command_buffer/service/command_buffer_task_executor.h
index f16e18f9..f4759e2 100644
--- a/gpu/command_buffer/service/command_buffer_task_executor.h
+++ b/gpu/command_buffer/service/command_buffer_task_executor.h
@@ -15,7 +15,6 @@
 #include "gpu/command_buffer/common/sync_token.h"
 #include "gpu/command_buffer/service/framebuffer_completeness_cache.h"
 #include "gpu/command_buffer/service/sequence_id.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/shader_translator_cache.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_preferences.h"
@@ -90,10 +89,6 @@
   }
   SyncPointManager* sync_point_manager() const { return sync_point_manager_; }
 
-  // Not const because these return inner pointers.
-  ServiceDiscardableManager* discardable_manager() {
-    return &discardable_manager_;
-  }
   gles2::ShaderTranslatorCache* shader_translator_cache() {
     return &shader_translator_cache_;
   }
@@ -114,7 +109,6 @@
   gl::GLSurfaceFormat share_group_surface_format_;
   std::unique_ptr<gles2::ProgramCache> owned_program_cache_;
   raw_ptr<gles2::ProgramCache> program_cache_;
-  ServiceDiscardableManager discardable_manager_;
   gles2::ShaderTranslatorCache shader_translator_cache_;
   gles2::FramebufferCompletenessCache framebuffer_completeness_cache_;
   raw_ptr<SharedImageManager> shared_image_manager_;
diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc
index 2387169..3f4a004 100644
--- a/gpu/command_buffer/service/context_group.cc
+++ b/gpu/command_buffer/service/context_group.cc
@@ -22,7 +22,6 @@
 #include "gpu/command_buffer/service/program_manager.h"
 #include "gpu/command_buffer/service/renderbuffer_manager.h"
 #include "gpu/command_buffer/service/sampler_manager.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/shader_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
 #include "gpu/command_buffer/service/texture_manager.h"
@@ -74,7 +73,6 @@
     const scoped_refptr<FeatureInfo>& feature_info,
     gl::ProgressReporter* progress_reporter,
     const GpuFeatureInfo& gpu_feature_info,
-    ServiceDiscardableManager* discardable_manager,
     SharedImageManager* shared_image_manager)
     : gpu_preferences_(gpu_preferences),
       memory_tracker_(std::move(memory_tracker)),
@@ -114,13 +112,11 @@
       passthrough_resources_(new PassthroughResources),
       progress_reporter_(progress_reporter),
       gpu_feature_info_(gpu_feature_info),
-      discardable_manager_(discardable_manager),
       shared_image_representation_factory_(
           std::make_unique<SharedImageRepresentationFactory>(
               shared_image_manager,
               memory_tracker_)),
       shared_image_manager_(shared_image_manager) {
-  DCHECK(discardable_manager);
   DCHECK(feature_info_);
 
   use_passthrough_cmd_decoder_ = gpu_preferences_.use_passthrough_cmd_decoder;
@@ -392,8 +388,7 @@
         memory_tracker_, feature_info_.get(), max_texture_size,
         max_cube_map_texture_size, max_rectangle_texture_size,
         max_3d_texture_size, max_array_texture_layers,
-        /*use_default_textures=*/false, progress_reporter_,
-        discardable_manager_);
+        /*use_default_textures=*/false, progress_reporter_);
   }
 
   const GLint kMinTextureImageUnits = 8;
diff --git a/gpu/command_buffer/service/context_group.h b/gpu/command_buffer/service/context_group.h
index 08e78d11..43841aa5 100644
--- a/gpu/command_buffer/service/context_group.h
+++ b/gpu/command_buffer/service/context_group.h
@@ -33,7 +33,6 @@
 struct GpuPreferences;
 class SharedImageManager;
 class SharedImageRepresentationFactory;
-class ServiceDiscardableManager;
 class DecoderContext;
 class MemoryTracker;
 
@@ -64,7 +63,6 @@
                const scoped_refptr<FeatureInfo>& feature_info,
                gl::ProgressReporter* progress_reporter,
                const GpuFeatureInfo& gpu_feature_info,
-               ServiceDiscardableManager* discardable_manager,
                SharedImageManager* shared_image_manager);
 
   ContextGroup(const ContextGroup&) = delete;
@@ -186,10 +184,6 @@
     return sampler_manager_.get();
   }
 
-  ServiceDiscardableManager* discardable_manager() const {
-    return discardable_manager_;
-  }
-
   SharedImageRepresentationFactory* shared_image_representation_factory()
       const {
     return shared_image_representation_factory_.get();
@@ -302,8 +296,6 @@
 
   GpuFeatureInfo gpu_feature_info_;
 
-  raw_ptr<ServiceDiscardableManager> discardable_manager_;
-
   std::unique_ptr<SharedImageRepresentationFactory>
       shared_image_representation_factory_;
 
diff --git a/gpu/command_buffer/service/context_group_unittest.cc b/gpu/command_buffer/service/context_group_unittest.cc
index 9576bcb..d59e3399 100644
--- a/gpu/command_buffer/service/context_group_unittest.cc
+++ b/gpu/command_buffer/service/context_group_unittest.cc
@@ -12,7 +12,6 @@
 #include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h"
 #include "gpu/command_buffer/service/gpu_service_test.h"
 #include "gpu/command_buffer/service/gpu_tracer.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/test_helper.h"
 #include "gpu/command_buffer/service/texture_manager.h"
@@ -36,7 +35,7 @@
 
 class ContextGroupTest : public GpuServiceTest {
  public:
-  ContextGroupTest() : discardable_manager_(gpu_preferences_) {}
+  ContextGroupTest() {}
 
  protected:
   void SetUp() override {
@@ -48,12 +47,11 @@
         gpu_preferences_, /*memory_tracker=*/nullptr,
         /*shader_translator_cache=*/nullptr,
         /*framebuffer_completeness_cache=*/nullptr, feature_info,
-        /*progress_reporter=*/nullptr, GpuFeatureInfo(), &discardable_manager_,
+        /*progress_reporter=*/nullptr, GpuFeatureInfo(),
         &shared_image_manager_));
   }
 
   GpuPreferences gpu_preferences_;
-  ServiceDiscardableManager discardable_manager_;
   SharedImageManager shared_image_manager_;
   FakeCommandBufferServiceBase command_buffer_service_;
   FakeDecoderClient client_;
diff --git a/gpu/command_buffer/service/framebuffer_manager_unittest.cc b/gpu/command_buffer/service/framebuffer_manager_unittest.cc
index 9f555f2..9a10785 100644
--- a/gpu/command_buffer/service/framebuffer_manager_unittest.cc
+++ b/gpu/command_buffer/service/framebuffer_manager_unittest.cc
@@ -19,7 +19,6 @@
 #include "gpu/command_buffer/service/gpu_service_test.h"
 #include "gpu/command_buffer/service/gpu_tracer.h"
 #include "gpu/command_buffer/service/renderbuffer_manager.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/test_helper.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "gpu/config/gpu_preferences.h"
@@ -49,13 +48,11 @@
 class FramebufferManagerTest : public GpuServiceTest {
  public:
   FramebufferManagerTest()
-      : manager_(1, 1, nullptr),
-        feature_info_(new FeatureInfo()),
-        discardable_manager_(GpuPreferences()) {
+      : manager_(1, 1, nullptr), feature_info_(new FeatureInfo()) {
     texture_manager_ = std::make_unique<TextureManager>(
         nullptr, feature_info_.get(), kMaxTextureSize, kMaxCubemapSize,
         kMaxRectangleTextureSize, kMax3DTextureSize, kMaxArrayTextureLayers,
-        kUseDefaultTextures, nullptr, &discardable_manager_);
+        kUseDefaultTextures, nullptr);
     renderbuffer_manager_ = std::make_unique<RenderbufferManager>(
         nullptr, kMaxRenderbufferSize, kMaxSamples, feature_info_.get());
   }
@@ -69,7 +66,6 @@
  protected:
   FramebufferManager manager_;
   scoped_refptr<FeatureInfo> feature_info_;
-  ServiceDiscardableManager discardable_manager_;
   std::unique_ptr<TextureManager> texture_manager_;
   std::unique_ptr<RenderbufferManager> renderbuffer_manager_;
 };
@@ -128,12 +124,11 @@
         manager_(kMaxDrawBuffers,
                  kMaxColorAttachments,
                  &framebuffer_completeness_cache_),
-        feature_info_(new FeatureInfo()),
-        discardable_manager_(GpuPreferences()) {
+        feature_info_(new FeatureInfo()) {
     texture_manager_ = std::make_unique<TextureManager>(
         nullptr, feature_info_.get(), kMaxTextureSize, kMaxCubemapSize,
         kMaxRectangleTextureSize, kMax3DTextureSize, kMaxArrayTextureLayers,
-        kUseDefaultTextures, nullptr, &discardable_manager_);
+        kUseDefaultTextures, nullptr);
     renderbuffer_manager_ = std::make_unique<RenderbufferManager>(
         nullptr, kMaxRenderbufferSize, kMaxSamples, feature_info_.get());
   }
@@ -175,7 +170,6 @@
   FramebufferManager manager_;
   raw_ptr<Framebuffer> framebuffer_;
   scoped_refptr<FeatureInfo> feature_info_;
-  ServiceDiscardableManager discardable_manager_;
   std::unique_ptr<TextureManager> texture_manager_;
   std::unique_ptr<RenderbufferManager> renderbuffer_manager_;
   std::unique_ptr<MockErrorState> error_state_;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index f9d513c3..fba1075 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -79,7 +79,6 @@
 #include "gpu/command_buffer/service/program_manager.h"
 #include "gpu/command_buffer/service/renderbuffer_manager.h"
 #include "gpu/command_buffer/service/sampler_manager.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/shader_manager.h"
 #include "gpu/command_buffer/service/shader_translator.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index cb440e0d..45dd1a3 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -145,8 +145,7 @@
       cached_stencil_front_mask_(static_cast<GLuint>(-1)),
       cached_stencil_back_mask_(static_cast<GLuint>(-1)),
       shader_language_version_(100),
-      shader_translator_cache_(gpu_preferences_),
-      discardable_manager_(gpu_preferences_) {
+      shader_translator_cache_(gpu_preferences_) {
   memset(immediate_buffer_, 0xEE, sizeof(immediate_buffer_));
 }
 
@@ -214,8 +213,7 @@
   group_ = MakeRefCounted<ContextGroup>(
       gpu_preferences_, memory_tracker_, &shader_translator_cache_,
       &framebuffer_completeness_cache_, feature_info,
-      /*progress_reporter=*/nullptr, gpu_feature_info, &discardable_manager_,
-      &shared_image_manager_);
+      /*progress_reporter=*/nullptr, gpu_feature_info, &shared_image_manager_);
 
   InSequence sequence;
 
@@ -2283,8 +2281,7 @@
     ContextType context_type)
     : context_type_(context_type),
       gpu_preferences_(GenerateGpuPreferencesForPassthroughTests()),
-      shader_translator_cache_(gpu_preferences_),
-      discardable_manager_(gpu_preferences_) {}
+      shader_translator_cache_(gpu_preferences_) {}
 
 GLES2DecoderPassthroughTestBase::~GLES2DecoderPassthroughTestBase() = default;
 
@@ -2329,8 +2326,7 @@
   group_ = MakeRefCounted<gles2::ContextGroup>(
       gpu_preferences_, /*memory_tracker=*/nullptr, &shader_translator_cache_,
       &framebuffer_completeness_cache_, feature_info,
-      /*progress_reporter=*/nullptr, GpuFeatureInfo(), &discardable_manager_,
-      &shared_image_manager_);
+      /*progress_reporter=*/nullptr, GpuFeatureInfo(), &shared_image_manager_);
 
   surface_ = gl::init::CreateOffscreenGLSurface(display_, gfx::Size(4, 4));
   context_ = gl::init::CreateGLContext(
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
index 86d8178..87409811 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
@@ -35,7 +35,6 @@
 #include "gpu/command_buffer/service/program_manager.h"
 #include "gpu/command_buffer/service/renderbuffer_manager.h"
 #include "gpu/command_buffer/service/sampler_manager.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/shader_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/test_helper.h"
@@ -793,7 +792,6 @@
   GpuPreferences gpu_preferences_;
   ShaderTranslatorCache shader_translator_cache_;
   FramebufferCompletenessCache framebuffer_completeness_cache_;
-  ServiceDiscardableManager discardable_manager_;
   SharedImageManager shared_image_manager_;
   scoped_refptr<ContextGroup> group_;
   MockGLStates gl_states_;
@@ -1011,7 +1009,6 @@
   GpuPreferences gpu_preferences_;
   ShaderTranslatorCache shader_translator_cache_;
   FramebufferCompletenessCache framebuffer_completeness_cache_;
-  ServiceDiscardableManager discardable_manager_;
   SharedImageManager shared_image_manager_;
 
   scoped_refptr<gl::GLSurface> surface_;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
index 651de4b..592e8bb 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
@@ -23,7 +23,6 @@
 #include "gpu/command_buffer/service/gles2_cmd_decoder_unittest.h"
 #include "gpu/command_buffer/service/mocks.h"
 #include "gpu/command_buffer/service/program_manager.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/test_image_backing.h"
 #include "gpu/command_buffer/service/test_helper.h"
diff --git a/gpu/command_buffer/service/service_discardable_manager.cc b/gpu/command_buffer/service/service_discardable_manager.cc
deleted file mode 100644
index ced6b8c..0000000
--- a/gpu/command_buffer/service/service_discardable_manager.cc
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "gpu/command_buffer/service/service_discardable_manager.h"
-
-#include <inttypes.h>
-
-#include "base/command_line.h"
-#include "base/memory/singleton.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/system/sys_info.h"
-#include "base/task/single_thread_task_runner.h"
-#include "base/trace_event/memory_dump_manager.h"
-#include "build/build_config.h"
-#include "gpu/command_buffer/service/gpu_switches.h"
-#include "gpu/command_buffer/service/texture_manager.h"
-#include "gpu/config/gpu_preferences.h"
-
-namespace gpu {
-
-size_t DiscardableCacheSizeLimit() {
-// Cache size values are designed to roughly correspond to existing image cache
-// sizes for 1-1.5 renderers. These will be updated as more types of data are
-// moved to this cache.
-#if BUILDFLAG(IS_ANDROID)
-  const size_t kLowEndCacheSizeBytes = 1024 * 1024;
-  const size_t kNormalCacheSizeBytes = 128 * 1024 * 1024;
-#else
-  const size_t kNormalCacheSizeBytes = 192 * 1024 * 1024;
-  const size_t kLargeCacheSizeBytes = 256 * 1024 * 1024;
-  // Device ram threshold at which we move from a normal cache to a large cache.
-  // While this is a GPU memory cache, we can't read GPU memory reliably, so we
-  // use system ram as a proxy.
-  constexpr base::ByteCount kLargeCacheSizeMemoryThreshold = base::GiB(4);
-#endif  // BUILDFLAG(IS_ANDROID)
-
-#if BUILDFLAG(IS_ANDROID)
-  if (base::SysInfo::IsLowEndDevice()) {
-    return kLowEndCacheSizeBytes;
-  } else {
-    return kNormalCacheSizeBytes;
-  }
-#else
-  if (base::SysInfo::AmountOfPhysicalMemory() <
-      kLargeCacheSizeMemoryThreshold) {
-    return kNormalCacheSizeBytes;
-  } else {
-    return kLargeCacheSizeBytes;
-  }
-#endif
-}
-
-size_t DiscardableCacheSizeLimitForPressure(
-    size_t base_cache_limit,
-    base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
-  switch (memory_pressure_level) {
-    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
-      return base_cache_limit;
-    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
-      // With moderate pressure, shrink to 1/4 our normal size.
-      return base_cache_limit / 4;
-    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
-      // With critical pressure, purge as much as possible.
-      return 0;
-
-    default:
-      NOTREACHED();
-  }
-}
-
-ServiceDiscardableManager::GpuDiscardableEntry::GpuDiscardableEntry(
-    ServiceDiscardableHandle handle,
-    size_t size)
-    : handle(handle), size(size) {}
-ServiceDiscardableManager::GpuDiscardableEntry::GpuDiscardableEntry(
-    const GpuDiscardableEntry& other) = default;
-ServiceDiscardableManager::GpuDiscardableEntry::GpuDiscardableEntry(
-    GpuDiscardableEntry&& other) = default;
-ServiceDiscardableManager::GpuDiscardableEntry::~GpuDiscardableEntry() =
-    default;
-
-ServiceDiscardableManager::ServiceDiscardableManager(
-    const GpuPreferences& preferences) {
-  // In certain cases, SingleThreadTaskRunner::CurrentDefaultHandle isn't set
-  // (Android Webview).  Don't register a dump provider in these cases.
-  if (base::SingleThreadTaskRunner::HasCurrentDefault()) {
-    base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
-        this, "gpu::ServiceDiscardableManager",
-        base::SingleThreadTaskRunner::GetCurrentDefault());
-  }
-}
-
-ServiceDiscardableManager::~ServiceDiscardableManager() {
-  base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
-      this);
-}
-
-bool ServiceDiscardableManager::OnMemoryDump(
-    const base::trace_event::MemoryDumpArgs& args,
-    base::trace_event::ProcessMemoryDump* pmd) {
-  using base::trace_event::MemoryAllocatorDump;
-  using base::trace_event::MemoryDumpLevelOfDetail;
-
-  if (args.level_of_detail == MemoryDumpLevelOfDetail::kBackground) {
-    std::string dump_name =
-        base::StringPrintf("gpu/discardable_cache/cache_0x%" PRIXPTR,
-                           reinterpret_cast<uintptr_t>(this));
-    MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name);
-    dump->AddScalar(MemoryAllocatorDump::kNameSize,
-                    MemoryAllocatorDump::kUnitsBytes, 0);
-
-    // Early out, no need for more detail in a BACKGROUND dump.
-    return true;
-  }
-
-  return true;
-}
-
-void ServiceDiscardableManager::OnTextureManagerDestruction(
-    gles2::TextureManager* texture_manager) {
-}
-
-void ServiceDiscardableManager::OnTextureDeleted(
-    uint32_t texture_id,
-    gles2::TextureManager* texture_manager) {
-}
-
-void ServiceDiscardableManager::OnContextLost() {
-}
-
-void ServiceDiscardableManager::OnTextureSizeChanged(
-    uint32_t texture_id,
-    gles2::TextureManager* texture_manager,
-    size_t new_size) {
-}
-
-void ServiceDiscardableManager::HandleMemoryPressure(
-    base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
-}
-
-void ServiceDiscardableManager::EnforceCacheSizeLimit(size_t limit) {
-}
-
-}  // namespace gpu
diff --git a/gpu/command_buffer/service/service_discardable_manager.h b/gpu/command_buffer/service/service_discardable_manager.h
deleted file mode 100644
index 08fe57a..0000000
--- a/gpu/command_buffer/service/service_discardable_manager.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef GPU_COMMAND_BUFFER_SERVICE_SERVICE_DISCARDABLE_MANAGER_H_
-#define GPU_COMMAND_BUFFER_SERVICE_SERVICE_DISCARDABLE_MANAGER_H_
-
-#include <vector>
-
-#include "base/containers/lru_cache.h"
-#include "base/memory/memory_pressure_listener.h"
-#include "base/memory/raw_ptr.h"
-#include "base/trace_event/memory_dump_provider.h"
-#include "gpu/command_buffer/common/discardable_handle.h"
-#include "gpu/command_buffer/service/context_group.h"
-#include "gpu/gpu_gles2_export.h"
-
-namespace gpu {
-struct GpuPreferences;
-namespace gles2 {
-class TextureManager;
-class TextureRef;
-}
-
-GPU_GLES2_EXPORT size_t DiscardableCacheSizeLimit();
-GPU_GLES2_EXPORT size_t DiscardableCacheSizeLimitForPressure(
-    size_t base_cache_limit,
-    base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
-
-class GPU_GLES2_EXPORT ServiceDiscardableManager
-    : public base::trace_event::MemoryDumpProvider {
- public:
-  explicit ServiceDiscardableManager(const GpuPreferences& preferences);
-
-  ServiceDiscardableManager(const ServiceDiscardableManager&) = delete;
-  ServiceDiscardableManager& operator=(const ServiceDiscardableManager&) =
-      delete;
-
-  ~ServiceDiscardableManager() override;
-
-  // base::trace_event::MemoryDumpProvider implementation.
-  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
-                    base::trace_event::ProcessMemoryDump* pmd) override;
-
-  // Returns all unlocked texture refs to the texture_manager for deletion.
-  // After this point, this class will have no references to the given
-  // |texture_manager|.
-  void OnTextureManagerDestruction(gles2::TextureManager* texture_manager);
-
-  // Called when a texture is deleted, to clean up state.
-  void OnTextureDeleted(uint32_t texture_id,
-                        gles2::TextureManager* texture_manager);
-
-  // Called by the TextureManager when a texture's size changes.
-  void OnTextureSizeChanged(uint32_t texture_id,
-                            gles2::TextureManager* texture_manager,
-                            size_t new_size);
-
-  // Called when all contexts with cached textures in this manager are lost.
-  void OnContextLost();
-
-  void HandleMemoryPressure(
-      base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
-
- private:
-  void EnforceCacheSizeLimit(size_t limit);
-
-  struct GpuDiscardableEntry {
-   public:
-    GpuDiscardableEntry(ServiceDiscardableHandle handle, size_t size);
-    GpuDiscardableEntry(const GpuDiscardableEntry& other);
-    GpuDiscardableEntry(GpuDiscardableEntry&& other);
-    ~GpuDiscardableEntry();
-
-    ServiceDiscardableHandle handle;
-    scoped_refptr<gles2::TextureRef> unlocked_texture_ref;
-    // The current ref count of this object with regards to command buffer
-    // execution. May be out of sync with the handle's refcount, as the handle
-    // can be locked out of band with the command buffer.
-    uint32_t service_ref_count_ = 1;
-    size_t size;
-  };
-  struct GpuDiscardableEntryKey {
-    uint32_t texture_id;
-    raw_ptr<gles2::TextureManager> texture_manager;
-  };
-  struct GpuDiscardableEntryKeyCompare {
-    bool operator()(const GpuDiscardableEntryKey& lhs,
-                    const GpuDiscardableEntryKey& rhs) const {
-      return std::tie(lhs.texture_manager, lhs.texture_id) <
-             std::tie(rhs.texture_manager, rhs.texture_id);
-    }
-  };
-  using EntryCache = base::LRUCache<GpuDiscardableEntryKey,
-                                    GpuDiscardableEntry,
-                                    GpuDiscardableEntryKeyCompare>;
-};
-
-}  // namespace gpu
-
-#endif  // GPU_COMMAND_BUFFER_SERVICE_SERVICE_DISCARDABLE_MANAGER_H_
diff --git a/gpu/command_buffer/service/service_transfer_cache.cc b/gpu/command_buffer/service/service_transfer_cache.cc
index 9eb4aa42..dce7604 100644
--- a/gpu/command_buffer/service/service_transfer_cache.cc
+++ b/gpu/command_buffer/service/service_transfer_cache.cc
@@ -17,7 +17,6 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "cc/paint/image_transfer_cache_entry.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/config/gpu_finch_features.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
@@ -36,6 +35,56 @@
 constexpr base::TimeDelta kOldEntryCutoffTimeDelta = base::Seconds(25);
 constexpr base::TimeDelta kOldEntryPruneInterval = base::Seconds(30);
 
+size_t DiscardableCacheSizeLimit() {
+// Cache size values are designed to roughly correspond to existing image cache
+// sizes for 1-1.5 renderers. These will be updated as more types of data are
+// moved to this cache.
+#if BUILDFLAG(IS_ANDROID)
+  const size_t kLowEndCacheSizeBytes = 1024 * 1024;
+  const size_t kNormalCacheSizeBytes = 128 * 1024 * 1024;
+#else
+  const size_t kNormalCacheSizeBytes = 192 * 1024 * 1024;
+  const size_t kLargeCacheSizeBytes = 256 * 1024 * 1024;
+  // Device ram threshold at which we move from a normal cache to a large cache.
+  // While this is a GPU memory cache, we can't read GPU memory reliably, so we
+  // use system ram as a proxy.
+  constexpr base::ByteCount kLargeCacheSizeMemoryThreshold = base::GiB(4);
+#endif  // BUILDFLAG(IS_ANDROID)
+
+#if BUILDFLAG(IS_ANDROID)
+  if (base::SysInfo::IsLowEndDevice()) {
+    return kLowEndCacheSizeBytes;
+  } else {
+    return kNormalCacheSizeBytes;
+  }
+#else
+  if (base::SysInfo::AmountOfPhysicalMemory() <
+      kLargeCacheSizeMemoryThreshold) {
+    return kNormalCacheSizeBytes;
+  } else {
+    return kLargeCacheSizeBytes;
+  }
+#endif
+}
+
+size_t DiscardableCacheSizeLimitForPressure(
+    size_t base_cache_limit,
+    base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
+  switch (memory_pressure_level) {
+    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
+      return base_cache_limit;
+    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
+      // With moderate pressure, shrink to 1/4 our normal size.
+      return base_cache_limit / 4;
+    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
+      // With critical pressure, purge as much as possible.
+      return 0;
+
+    default:
+      NOTREACHED();
+  }
+}
+
 // Alias the image entry to its skia counterpart, taking ownership of the
 // memory and preventing double counting.
 //
diff --git a/gpu/command_buffer/service/service_utils.cc b/gpu/command_buffer/service/service_utils.cc
index cd93ac0..18f0918 100644
--- a/gpu/command_buffer/service/service_utils.cc
+++ b/gpu/command_buffer/service/service_utils.cc
@@ -189,10 +189,19 @@
     attribs.allow_client_arrays = true;
   }
 
-  bool force_es2_context =
-      features::UseGles2ForOopR() && use_passthrough_cmd_decoder;
-
-  attribs.client_major_es_version = force_es2_context ? 2 : 3;
+  // Tests fail with GLES3 on Android emulators (crbug.com/1423712). Forcing the
+  // context to ES2 works around this issue at least when using the passthrough
+  // command decoder (it's unfortunately not guaranteed to work with the
+  // validating decoder as native GL drivers tend to return GLES3 contexts if
+  // they support GLES3 even if the client has requested GLES2, but it won't
+  // make any things worse there either).
+  // TODO(crbug.com/444049511): Eliminate the need for this workaround as part
+  // of eliminating GLES2 support in Chrome altogether.
+#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_X86_FAMILY)
+  attribs.client_major_es_version = 2;
+#else
+  attribs.client_major_es_version = 3;
+#endif
   attribs.client_minor_es_version = 0;
 
   return attribs;
diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/d3d_image_backing_factory.cc
index a7024695..0aa7f21 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing_factory.cc
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing_factory.cc
@@ -17,6 +17,7 @@
 #include "gpu/command_buffer/service/dxgi_shared_handle_manager.h"
 #include "gpu/command_buffer/service/shared_image/d3d_image_backing.h"
 #include "gpu/command_buffer/service/shared_image/d3d_image_utils.h"
+#include "gpu/command_buffer/service/shared_image/gpu_memory_buffer_factory_dxgi.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/config/gpu_finch_features.h"
 #include "ui/gfx/buffer_format_util.h"
@@ -34,6 +35,12 @@
 
 namespace {
 
+GpuMemoryBufferFactoryDXGI* GetGpuMemoryBufferFactoryDXGI(
+    scoped_refptr<base::SingleThreadTaskRunner> io_runner) {
+  static auto* factory = new GpuMemoryBufferFactoryDXGI(io_runner);
+  return factory;
+}
+
 // Formats supported by CreateSharedImage() for uploading initial data.
 bool IsFormatSupportedForInitialData(viz::SharedImageFormat format) {
   // The set of formats is artificially limited to avoid needing to handle
@@ -218,6 +225,26 @@
     D3DImageBackingFactory::SwapChainBackings&&) = default;
 
 // static
+gfx::GpuMemoryBufferHandle D3DImageBackingFactory::CreateGpuMemoryBufferHandle(
+    scoped_refptr<base::SingleThreadTaskRunner> io_runner,
+    const gfx::Size& size,
+    viz::SharedImageFormat format,
+    gfx::BufferUsage usage) {
+  return GetGpuMemoryBufferFactoryDXGI(io_runner)->CreateNativeGmbHandle(
+      size, format, usage);
+}
+
+// static
+bool D3DImageBackingFactory::CopyNativeBufferToSharedMemoryAsync(
+    scoped_refptr<base::SingleThreadTaskRunner> io_runner,
+    gfx::GpuMemoryBufferHandle buffer_handle,
+    base::UnsafeSharedMemoryRegion shared_memory) {
+  return GetGpuMemoryBufferFactoryDXGI(io_runner)
+      ->FillSharedMemoryRegionWithBufferContents(std::move(buffer_handle),
+                                                 std::move(shared_memory));
+}
+
+// static
 bool D3DImageBackingFactory::IsD3DSharedImageSupported(
     ID3D11Device* d3d11_device,
     const GpuPreferences& gpu_preferences) {
diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing_factory.h b/gpu/command_buffer/service/shared_image/d3d_image_backing_factory.h
index f55f9ab..a320320 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing_factory.h
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing_factory.h
@@ -15,6 +15,8 @@
 #include <memory>
 #include <optional>
 
+#include "base/memory/scoped_refptr.h"
+#include "base/task/single_thread_task_runner.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_backing_factory.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
@@ -48,6 +50,17 @@
 
   ~D3DImageBackingFactory() override;
 
+  static gfx::GpuMemoryBufferHandle CreateGpuMemoryBufferHandle(
+      scoped_refptr<base::SingleThreadTaskRunner> io_runner,
+      const gfx::Size& size,
+      viz::SharedImageFormat format,
+      gfx::BufferUsage usage);
+
+  static bool CopyNativeBufferToSharedMemoryAsync(
+      scoped_refptr<base::SingleThreadTaskRunner> io_runner,
+      gfx::GpuMemoryBufferHandle buffer_handle,
+      base::UnsafeSharedMemoryRegion shared_memory);
+
   // Returns true if D3D shared images are supported and this factory should be
   // used. Generally this means Skia-GL, passthrough decoder, and ANGLE-D3D11.
   static bool IsD3DSharedImageSupported(ID3D11Device* d3d11_device,
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing.h b/gpu/command_buffer/service/shared_image/iosurface_image_backing.h
index f690cca..952909d 100644
--- a/gpu/command_buffer/service/shared_image/iosurface_image_backing.h
+++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing.h
@@ -275,8 +275,10 @@
                                   uint64_t signal_value,
                                   bool readonly)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
-  template <typename Fn>
-  void ProcessSharedEventsForBeginAccess(bool readonly, const Fn& fn)
+  void ProcessSharedEventsForBeginAccess(
+      bool readonly,
+      base::FunctionRef<void(id<MTLSharedEvent> shared_event,
+                             uint64_t signaled_value)> process_fn)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
   // Guarded by ScopedIOSurfaceLock instead of |lock_| for memory access.
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
index 15d1fb3..d060768a 100644
--- a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
+++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
@@ -2183,14 +2183,15 @@
   it->second = std::max(it->second, signal_value);
 }
 
-template <typename Fn>
-void IOSurfaceImageBacking::ProcessSharedEventsForBeginAccess(bool readonly,
-                                                              const Fn& fn) {
+void IOSurfaceImageBacking::ProcessSharedEventsForBeginAccess(
+    bool readonly,
+    base::FunctionRef<void(id<MTLSharedEvent> shared_event,
+                           uint64_t signaled_value)> process_fn) {
   AssertLockAcquired();
 
   // Always need wait on exclusive access end events.
   for (const auto& [shared_event, signal_value] : exclusive_shared_events_) {
-    fn(shared_event.get(), signal_value);
+    process_fn(shared_event.get(), signal_value);
   }
 
   if (!readonly) {
@@ -2198,7 +2199,7 @@
     // should be waited on as well.
     for (const auto& [shared_event, signal_value] :
          non_exclusive_shared_events_) {
-      fn(shared_event.get(), signal_value);
+      process_fn(shared_event.get(), signal_value);
     }
 
     // Clear events, since this read-write (exclusive) access will provide an
diff --git a/gpu/command_buffer/service/shared_image/shared_image_factory.cc b/gpu/command_buffer/service/shared_image/shared_image_factory.cc
index 601604d..a12ceb0 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_factory.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_factory.cc
@@ -78,7 +78,6 @@
 #include "gpu/command_buffer/service/dxgi_shared_handle_manager.h"
 #include "gpu/command_buffer/service/shared_image/d3d_image_backing_factory.h"
 #include "gpu/command_buffer/service/shared_image/dcomp_image_backing_factory.h"
-#include "gpu/command_buffer/service/shared_image/gpu_memory_buffer_factory_dxgi.h"
 #include "ui/gl/direct_composition_support.h"
 #include "ui/gl/gl_angle_util_win.h"
 #endif  // BUILDFLAG(IS_WIN)
@@ -718,9 +717,8 @@
   return OzoneImageBackingFactory::CreateGpuMemoryBufferHandle(
       shared_image_manager_->vulkan_context_provider(), size, format, usage);
 #else
-  auto* gmb_factory = shared_image_manager_->gpu_memory_buffer_factory();
-  CHECK(gmb_factory);
-  return gmb_factory->CreateNativeGmbHandle(size, format, usage);
+  return D3DImageBackingFactory::CreateGpuMemoryBufferHandle(
+      shared_image_manager_->io_runner(), size, format, usage);
 #endif
 }
 #endif
@@ -729,10 +727,9 @@
     gfx::GpuMemoryBufferHandle buffer_handle,
     base::UnsafeSharedMemoryRegion shared_memory) {
 #if BUILDFLAG(IS_WIN)
-  auto* gmb_factory = shared_image_manager_->gpu_memory_buffer_factory();
-  CHECK(gmb_factory);
-  return gmb_factory->FillSharedMemoryRegionWithBufferContents(
-      std::move(buffer_handle), std::move(shared_memory));
+  return D3DImageBackingFactory::CopyNativeBufferToSharedMemoryAsync(
+      shared_image_manager_->io_runner(), std::move(buffer_handle),
+      std::move(shared_memory));
 #else
   return false;
 #endif
diff --git a/gpu/command_buffer/service/shared_image/shared_image_manager.cc b/gpu/command_buffer/service/shared_image/shared_image_manager.cc
index f25eafd..5804eff 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_manager.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_manager.cc
@@ -30,7 +30,6 @@
 
 #if BUILDFLAG(IS_WIN)
 #include "gpu/command_buffer/service/dxgi_shared_handle_manager.h"
-#include "gpu/command_buffer/service/shared_image/gpu_memory_buffer_factory_dxgi.h"
 #include "ui/gfx/win/d3d_shared_fence.h"
 #include "ui/gl/direct_composition_support.h"
 #include "ui/gl/gl_angle_util_win.h"
@@ -258,8 +257,7 @@
 #endif
 #if BUILDFLAG(IS_WIN)
       ,
-      gpu_memory_buffer_factory_(
-          std::make_unique<GpuMemoryBufferFactoryDXGI>(std::move(io_runner)))
+      io_runner_(std::move(io_runner))
 #endif
 {
   DCHECK(!display_context_on_another_thread || thread_safe);
diff --git a/gpu/command_buffer/service/shared_image/shared_image_manager.h b/gpu/command_buffer/service/shared_image/shared_image_manager.h
index c15f94a..57af166 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_manager.h
+++ b/gpu/command_buffer/service/shared_image/shared_image_manager.h
@@ -28,8 +28,6 @@
 namespace gfx {
 class D3DSharedFence;
 }
-
-#include "gpu/command_buffer/service/shared_image/gpu_memory_buffer_factory_dxgi.h"
 #endif
 
 namespace gpu {
@@ -164,9 +162,7 @@
   }
 
 #if BUILDFLAG(IS_WIN)
-  GpuMemoryBufferFactoryDXGI* gpu_memory_buffer_factory() {
-    return gpu_memory_buffer_factory_.get();
-  }
+  scoped_refptr<base::SingleThreadTaskRunner> io_runner() { return io_runner_; }
 #endif
 
 #if BUILDFLAG(IS_OZONE)
@@ -209,6 +205,7 @@
 
 #if BUILDFLAG(IS_WIN)
   scoped_refptr<DXGISharedHandleManager> dxgi_shared_handle_manager_;
+  scoped_refptr<base::SingleThreadTaskRunner> io_runner_;
 #endif
 
 #if BUILDFLAG(IS_OZONE)
@@ -216,10 +213,6 @@
   scoped_refptr<viz::VulkanContextProvider> vulkan_context_provider_;
 #endif
 
-#if BUILDFLAG(IS_WIN)
-  std::unique_ptr<GpuMemoryBufferFactoryDXGI> gpu_memory_buffer_factory_;
-#endif
-
   THREAD_CHECKER(thread_checker_);
 };
 
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index 582ab31..ee508252 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -37,7 +37,6 @@
 #include "gpu/command_buffer/service/feature_info.h"
 #include "gpu/command_buffer/service/framebuffer_manager.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_enums.h"
 #include "ui/gl/gl_implementation.h"
@@ -478,10 +477,6 @@
 }
 
 void TextureManager::Destroy() {
-  // Retreive any outstanding unlocked textures from the discardable manager so
-  // we can clean them up here.
-  discardable_manager_->OnTextureManagerDestruction(this);
-
   while (!textures_.empty()) {
     textures_.erase(textures_.begin());
     if (progress_reporter_)
@@ -1883,8 +1878,7 @@
                                GLint max_3d_texture_size,
                                GLint max_array_texture_layers,
                                bool use_default_textures,
-                               gl::ProgressReporter* progress_reporter,
-                               ServiceDiscardableManager* discardable_manager)
+                               gl::ProgressReporter* progress_reporter)
     : memory_type_tracker_(new MemoryTypeTracker(std::move(memory_tracker))),
       feature_info_(feature_info),
       max_texture_size_(max_texture_size),
@@ -1910,8 +1904,7 @@
       texture_count_(0),
       have_context_(true),
       current_service_id_generation_(0),
-      progress_reporter_(progress_reporter),
-      discardable_manager_(discardable_manager) {
+      progress_reporter_(progress_reporter) {
   for (int ii = 0; ii < kNumDefaultTextures; ++ii) {
     black_texture_ids_[ii] = 0;
   }
@@ -2137,8 +2130,6 @@
   Texture* texture = ref->texture();
   texture->SetLevelInfo(target, level, internal_format, width, height, depth,
                         border, format, type, cleared_rect);
-  discardable_manager_->OnTextureSizeChanged(ref->client_id(), this,
-                                             texture->estimated_size());
 }
 
 TextureRef* TextureManager::Consume(
@@ -2284,7 +2275,6 @@
 void TextureManager::RemoveTexture(GLuint client_id) {
   TextureMap::iterator it = textures_.find(client_id);
   if (it != textures_.end()) {
-    discardable_manager_->OnTextureDeleted(client_id, this);
     it->second->reset_client_id();
     textures_.erase(it);
   }
@@ -2315,9 +2305,6 @@
   }
   num_uncleared_mips_ -= texture->num_uncleared_mips();
   DCHECK_GE(num_uncleared_mips_, 0);
-
-  if (ref->client_id())
-    discardable_manager_->OnTextureDeleted(ref->client_id(), this);
 }
 
 MemoryTypeTracker* TextureManager::GetMemTracker() {
diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h
index 7ebee60..1c4ca62 100644
--- a/gpu/command_buffer/service/texture_manager.h
+++ b/gpu/command_buffer/service/texture_manager.h
@@ -38,7 +38,6 @@
 
 namespace gpu {
 class DecoderContext;
-class ServiceDiscardableManager;
 
 namespace gles2 {
 struct ContextState;
@@ -748,8 +747,7 @@
                  GLsizei max_3d_texture_size,
                  GLsizei max_array_texture_layers,
                  bool use_default_textures,
-                 gl::ProgressReporter* progress_reporter,
-                 ServiceDiscardableManager* discardable_manager);
+                 gl::ProgressReporter* progress_reporter);
 
   TextureManager(const TextureManager&) = delete;
   TextureManager& operator=(const TextureManager&) = delete;
@@ -1248,8 +1246,6 @@
   // preventing time-outs when destruction takes a long time. May be null when
   // using in-process command buffer.
   raw_ptr<gl::ProgressReporter> progress_reporter_;
-
-  raw_ptr<ServiceDiscardableManager> discardable_manager_;
 };
 
 }  // namespace gles2
diff --git a/gpu/command_buffer/service/texture_manager_unittest.cc b/gpu/command_buffer/service/texture_manager_unittest.cc
index 6025943..0928dab8 100644
--- a/gpu/command_buffer/service/texture_manager_unittest.cc
+++ b/gpu/command_buffer/service/texture_manager_unittest.cc
@@ -21,7 +21,6 @@
 #include "gpu/command_buffer/service/gpu_tracer.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/command_buffer/service/mocks.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/gl_mock.h"
@@ -68,7 +67,7 @@
   static const bool kUseDefaultTextures = false;
 
   TextureManagerTestBase(ContextType context_type)
-      : context_type_(context_type), discardable_manager_(GpuPreferences()) {
+      : context_type_(context_type) {
     DCHECK(context_type == CONTEXT_TYPE_OPENGLES2 ||
            context_type == CONTEXT_TYPE_OPENGLES3);
     GpuDriverBugWorkarounds gpu_driver_bug_workaround;
@@ -88,7 +87,7 @@
     manager_.reset(new TextureManager(
         nullptr, feature_info_.get(), kMaxTextureSize, kMaxCubeMapTextureSize,
         kMaxRectangleTextureSize, kMax3DTextureSize, kMaxArrayTextureLayers,
-        kUseDefaultTextures, nullptr, &discardable_manager_));
+        kUseDefaultTextures, nullptr));
     SetupFeatureInfo("", gl_version, context_type_);
     TestHelper::SetupTextureManagerInitExpectations(gl_.get(), es3, es3, {},
                                                     kUseDefaultTextures);
@@ -130,7 +129,6 @@
 
   ContextType context_type_;
   scoped_refptr<FeatureInfo> feature_info_;
-  ServiceDiscardableManager discardable_manager_;
   std::unique_ptr<TextureManager> manager_;
   std::unique_ptr<MockErrorState> error_state_;
 };
@@ -231,7 +229,7 @@
   TextureManager manager(nullptr, feature_info_.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         use_default_textures, nullptr, &discardable_manager_);
+                         use_default_textures, nullptr);
   manager.Initialize();
 
   EXPECT_TRUE(manager.GetDefaultTextureInfo(GL_TEXTURE_2D) != nullptr);
@@ -251,7 +249,7 @@
   TextureManager manager(nullptr, feature_info_.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         use_default_textures, nullptr, &discardable_manager_);
+                         use_default_textures, nullptr);
   manager.Initialize();
 
   EXPECT_TRUE(manager.GetDefaultTextureInfo(GL_TEXTURE_2D) == nullptr);
@@ -270,7 +268,7 @@
   TextureManager manager(nullptr, feature_info_.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         use_default_textures, nullptr, &discardable_manager_);
+                         use_default_textures, nullptr);
   manager.Initialize();
 
   EXPECT_TRUE(manager.GetDefaultTextureInfo(GL_TEXTURE_3D) != nullptr);
@@ -287,7 +285,7 @@
   TextureManager manager(nullptr, feature_info_.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         use_default_textures, nullptr, &discardable_manager_);
+                         use_default_textures, nullptr);
   manager.Initialize();
 
   EXPECT_TRUE(manager.GetDefaultTextureInfo(GL_TEXTURE_3D) == nullptr);
@@ -303,7 +301,7 @@
   TextureManager manager(nullptr, feature_info_.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         kUseDefaultTextures, nullptr, &discardable_manager_);
+                         kUseDefaultTextures, nullptr);
   manager.Initialize();
   const GLuint kClient1Id = 1;
   const GLuint kService1Id = 11;
@@ -329,7 +327,7 @@
   TextureManager manager(nullptr, feature_info_.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         kUseDefaultTextures, nullptr, &discardable_manager_);
+                         kUseDefaultTextures, nullptr);
   manager.Initialize();
   // Check we can create texture.
   manager.CreateTexture(kClient1Id, kService1Id);
@@ -484,7 +482,7 @@
   TextureManager manager(nullptr, feature_info.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         kUseDefaultTextures, nullptr, &discardable_manager_);
+                         kUseDefaultTextures, nullptr);
   // Check NPOT width on level 0
   EXPECT_TRUE(manager.ValidForTarget(GL_TEXTURE_2D, 0, 5, 2, 1));
   // Check NPOT height on level 0
@@ -510,9 +508,7 @@
   static const GLuint kService1Id = 11;
   static const bool kUseDefaultTextures = false;
 
-  TextureTestBase()
-      : feature_info_(new FeatureInfo()),
-        discardable_manager_(GpuPreferences()) {}
+  TextureTestBase() : feature_info_(new FeatureInfo()) {}
   ~TextureTestBase() override { texture_ref_ = nullptr; }
 
  protected:
@@ -526,8 +522,7 @@
     manager_.reset(new TextureManager(
         std::move(memory_tracker), feature_info_.get(), kMaxTextureSize,
         kMaxCubeMapTextureSize, kMaxRectangleTextureSize, kMax3DTextureSize,
-        kMaxArrayTextureLayers, kUseDefaultTextures, nullptr,
-        &discardable_manager_));
+        kMaxArrayTextureLayers, kUseDefaultTextures, nullptr));
     decoder_.reset(new ::testing::StrictMock<MockGLES2Decoder>(
         &client_, &command_buffer_service_, &outputter_));
     error_state_.reset(new ::testing::StrictMock<MockErrorState>());
@@ -568,7 +563,6 @@
   std::unique_ptr<MockGLES2Decoder> decoder_;
   std::unique_ptr<MockErrorState> error_state_;
   scoped_refptr<FeatureInfo> feature_info_;
-  ServiceDiscardableManager discardable_manager_;
   std::unique_ptr<TextureManager> manager_;
   scoped_refptr<TextureRef> texture_ref_;
 };
@@ -909,7 +903,7 @@
   TextureManager manager(nullptr, feature_info.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         kUseDefaultTextures, nullptr, &discardable_manager_);
+                         kUseDefaultTextures, nullptr);
   manager.CreateTexture(kClient1Id, kService1Id);
   TextureRef* texture_ref = manager.GetTexture(kClient1Id);
   ASSERT_TRUE(texture_ref != nullptr);
@@ -1208,7 +1202,7 @@
   TextureManager manager(nullptr, feature_info.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         kUseDefaultTextures, nullptr, &discardable_manager_);
+                         kUseDefaultTextures, nullptr);
   manager.CreateTexture(kClient1Id, kService1Id);
   TextureRef* texture_ref = manager.GetTexture(kClient1Id);
   ASSERT_TRUE(texture_ref != nullptr);
@@ -1238,7 +1232,7 @@
   TextureManager manager(nullptr, feature_info.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         kUseDefaultTextures, nullptr, &discardable_manager_);
+                         kUseDefaultTextures, nullptr);
   manager.CreateTexture(kClient1Id, kService1Id);
   TextureRef* texture_ref = manager.GetTexture(kClient1Id);
   ASSERT_TRUE(texture_ref != nullptr);
@@ -1260,7 +1254,7 @@
   TextureManager manager(nullptr, feature_info.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         kUseDefaultTextures, nullptr, &discardable_manager_);
+                         kUseDefaultTextures, nullptr);
   manager.CreateTexture(kClient1Id, kService1Id);
   TextureRef* texture_ref = manager.GetTexture(kClient1Id);
   ASSERT_TRUE(texture_ref != nullptr);
@@ -1290,7 +1284,7 @@
   TextureManager manager(nullptr, feature_info.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         kUseDefaultTextures, nullptr, &discardable_manager_);
+                         kUseDefaultTextures, nullptr);
   manager.CreateTexture(kClient1Id, kService1Id);
   TextureRef* texture_ref = manager.GetTexture(kClient1Id);
   ASSERT_TRUE(texture_ref != nullptr);
@@ -1312,7 +1306,7 @@
   TextureManager manager(nullptr, feature_info.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         kUseDefaultTextures, nullptr, &discardable_manager_);
+                         kUseDefaultTextures, nullptr);
   manager.CreateTexture(kClient1Id, kService1Id);
   TextureRef* texture_ref = manager.GetTexture(kClient1Id);
   ASSERT_TRUE(texture_ref != nullptr);
@@ -1332,7 +1326,7 @@
   TextureManager manager(nullptr, feature_info.get(), kMaxTextureSize,
                          kMaxCubeMapTextureSize, kMaxRectangleTextureSize,
                          kMax3DTextureSize, kMaxArrayTextureLayers,
-                         kUseDefaultTextures, nullptr, &discardable_manager_);
+                         kUseDefaultTextures, nullptr);
   manager.CreateTexture(kClient1Id, kService1Id);
   TextureRef* texture_ref = manager.GetTexture(kClient1Id);
   ASSERT_TRUE(texture_ref != nullptr);
@@ -1978,7 +1972,6 @@
 
   SharedTextureTest()
       : feature_info_(new FeatureInfo()),
-        discardable_manager_(GpuPreferences()),
         memory_tracker1_(base::MakeRefCounted<MemoryTracker>()),
         memory_tracker2_(base::MakeRefCounted<MemoryTracker>()) {}
 
@@ -1986,22 +1979,22 @@
 
   void SetUp() override {
     GpuServiceTest::SetUp();
-    texture_manager1_.reset(new TextureManager(
-        memory_tracker1_, feature_info_.get(),
-        TextureManagerTest::kMaxTextureSize,
-        TextureManagerTest::kMaxCubeMapTextureSize,
-        TextureManagerTest::kMaxRectangleTextureSize,
-        TextureManagerTest::kMax3DTextureSize,
-        TextureManagerTest::kMaxArrayTextureLayers, kUseDefaultTextures,
-        nullptr, &discardable_manager_));
-    texture_manager2_.reset(new TextureManager(
-        memory_tracker2_, feature_info_.get(),
-        TextureManagerTest::kMaxTextureSize,
-        TextureManagerTest::kMaxCubeMapTextureSize,
-        TextureManagerTest::kMaxRectangleTextureSize,
-        TextureManagerTest::kMax3DTextureSize,
-        TextureManagerTest::kMaxArrayTextureLayers, kUseDefaultTextures,
-        nullptr, &discardable_manager_));
+    texture_manager1_.reset(
+        new TextureManager(memory_tracker1_, feature_info_.get(),
+                           TextureManagerTest::kMaxTextureSize,
+                           TextureManagerTest::kMaxCubeMapTextureSize,
+                           TextureManagerTest::kMaxRectangleTextureSize,
+                           TextureManagerTest::kMax3DTextureSize,
+                           TextureManagerTest::kMaxArrayTextureLayers,
+                           kUseDefaultTextures, nullptr));
+    texture_manager2_.reset(
+        new TextureManager(memory_tracker2_, feature_info_.get(),
+                           TextureManagerTest::kMaxTextureSize,
+                           TextureManagerTest::kMaxCubeMapTextureSize,
+                           TextureManagerTest::kMaxRectangleTextureSize,
+                           TextureManagerTest::kMax3DTextureSize,
+                           TextureManagerTest::kMaxArrayTextureLayers,
+                           kUseDefaultTextures, nullptr));
     SetupFeatureInfo("", "OpenGL ES 2.0", CONTEXT_TYPE_OPENGLES2);
     TestHelper::SetupTextureManagerInitExpectations(gl_.get(), false, false, {},
                                                     kUseDefaultTextures);
@@ -2041,7 +2034,6 @@
   }
 
   scoped_refptr<FeatureInfo> feature_info_;
-  ServiceDiscardableManager discardable_manager_;
 
   scoped_refptr<MemoryTracker> memory_tracker1_;
   std::unique_ptr<TextureManager> texture_manager1_;
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index cf360ba..a4cf2a95 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -1256,10 +1256,13 @@
     force_fallback_adapter_ = true;
   }
 
-  // Create a Chrome-side EGL context. This isn't actually used by Dawn,
-  // but it prevents rendering artifacts in Chrome. This workaround should
-  // be revisited once EGL context creation is reworked. See crbug.com/1465911
-  if (use_webgpu_adapter_ == WebGPUAdapterName::kOpenGLES) {
+  // Create a Chrome-side EGL context. Dawn actually creates its own
+  // EGL contexts per-device, but since Chrome is unaware of those
+  // contexts, this wrapper context keeps Chrome's virtual context
+  // bookkeeping up-to-date.
+  // This is only an issue for native EGL/GLES, not ANGLE (which is
+  // aware of the the EGL contexts created by Dawn).
+  if (gl::GetGLImplementation() == gl::kGLImplementationEGLGLES2) {
     scoped_refptr<gl::GLSurface> gl_surface(new gl::SurfacelessEGL(
         gl::GLSurfaceEGL::GetGLDisplayEGL(), gfx::Size(1, 1)));
     gl::GLContextAttribs attribs;
@@ -1268,7 +1271,6 @@
     gl_context_ = new gl::GLContextEGL(nullptr);
     gl_context_->Initialize(gl_surface.get(), attribs);
     DCHECK(gl_context_->default_surface());
-    gl_context_->MakeCurrentDefault();
   }
   return ContextResult::kSuccess;
 }
diff --git a/gpu/command_buffer/tests/decoder_perftest.cc b/gpu/command_buffer/tests/decoder_perftest.cc
index cb80c1d..814524d 100644
--- a/gpu/command_buffer/tests/decoder_perftest.cc
+++ b/gpu/command_buffer/tests/decoder_perftest.cc
@@ -27,7 +27,6 @@
 #include "gpu/command_buffer/service/gpu_tracer.h"
 #include "gpu/command_buffer/service/logger.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/sync_point_manager.h"
@@ -157,7 +156,6 @@
   RecordReplayContext()
       : gpu_preferences_(GetGpuPreferences()),
         share_group_(new gl::GLShareGroup),
-        discardable_manager_(gpu::GpuPreferences()),
         translator_cache_(gpu_preferences_) {
     if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-stub")) {
       surface_ = new gl::GLSurfaceStub;
@@ -182,7 +180,7 @@
     auto context_group = base::MakeRefCounted<gles2::ContextGroup>(
         gpu_preferences_, /*memory_tracker=*/nullptr, &translator_cache_,
         &completeness_cache_, feature_info,
-        /*progress_reporter=*/nullptr, GpuFeatureInfo(), &discardable_manager_,
+        /*progress_reporter=*/nullptr, GpuFeatureInfo(),
         &shared_image_manager_);
     command_buffer_ = std::make_unique<RecordReplayCommandBuffer>();
 
@@ -308,7 +306,6 @@
   GpuPreferences gpu_preferences_;
 
   scoped_refptr<gl::GLShareGroup> share_group_;
-  ServiceDiscardableManager discardable_manager_;
   SharedImageManager shared_image_manager_;
 
   scoped_refptr<gl::GLSurface> surface_;
diff --git a/gpu/command_buffer/tests/fuzzer_main.cc b/gpu/command_buffer/tests/fuzzer_main.cc
index 54e484a..e629808 100644
--- a/gpu/command_buffer/tests/fuzzer_main.cc
+++ b/gpu/command_buffer/tests/fuzzer_main.cc
@@ -35,7 +35,6 @@
 #include "gpu/command_buffer/service/gpu_tracer.h"
 #include "gpu/command_buffer/service/logger.h"
 #include "gpu/command_buffer/service/raster_decoder.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
@@ -349,9 +348,6 @@
 #else
 #error invalid configuration
 #endif
-    discardable_manager_ =
-        std::make_unique<ServiceDiscardableManager>(gpu_preferences_);
-
     if (gpu_preferences_.use_passthrough_cmd_decoder)
       recreate_context_ = true;
 
@@ -455,7 +451,7 @@
             gpu_preferences_, /*memory_tracker=*/nullptr, &translator_cache_,
             &completeness_cache_, decoder_feature_info,
             /*progress_reporter=*/nullptr, gpu_feature_info,
-            discardable_manager_.get(), shared_image_manager_.get());
+            shared_image_manager_.get());
     auto* context = context_.get();
     decoder_ = gles2::GLES2Decoder::Create(command_buffer_.get(),
                                            command_buffer_->service(),
@@ -642,7 +638,6 @@
 
   gles2::TraceOutputter outputter_;
   scoped_refptr<gl::GLShareGroup> share_group_;
-  std::unique_ptr<ServiceDiscardableManager> discardable_manager_;
   std::unique_ptr<SharedImageManager> shared_image_manager_;
   std::unique_ptr<SharedImageFactory> shared_image_factory_;
 
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc
index 3d88eb0f..742261b 100644
--- a/gpu/command_buffer/tests/gl_manager.cc
+++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -167,8 +167,6 @@
 
   translator_cache_ =
       std::make_unique<gles2::ShaderTranslatorCache>(gpu_preferences_);
-  discardable_manager_ =
-      std::make_unique<ServiceDiscardableManager>(gpu_preferences_);
 
   if (!context_group) {
     GpuFeatureInfo gpu_feature_info;
@@ -180,7 +178,7 @@
         gpu_preferences_, /*memory_tracker=*/nullptr, translator_cache_.get(),
         &completeness_cache_, feature_info,
         /*progress_reporter=*/nullptr, gpu_feature_info,
-        discardable_manager_.get(), &shared_image_manager_);
+        &shared_image_manager_);
   }
 
   command_buffer_.reset(
diff --git a/gpu/command_buffer/tests/gl_manager.h b/gpu/command_buffer/tests/gl_manager.h
index e0c6d61b..89c0c6a 100644
--- a/gpu/command_buffer/tests/gl_manager.h
+++ b/gpu/command_buffer/tests/gl_manager.h
@@ -16,8 +16,9 @@
 #include "gpu/command_buffer/client/shared_memory_limits.h"
 #include "gpu/command_buffer/common/context_creation_attribs.h"
 #include "gpu/command_buffer/service/feature_info.h"
+#include "gpu/command_buffer/service/framebuffer_completeness_cache.h"
 #include "gpu/command_buffer/service/gpu_tracer.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
+#include "gpu/command_buffer/service/shader_translator_cache.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_preferences.h"
@@ -107,10 +108,6 @@
 
   gl::GLContext* context() { return context_.get(); }
 
-  ServiceDiscardableManager* discardable_manager() {
-    return discardable_manager_.get();
-  }
-
   const GpuDriverBugWorkarounds& workarounds() const;
   const gpu::GpuPreferences& gpu_preferences() const {
     return gpu_preferences_;
@@ -153,7 +150,6 @@
   gpu::GpuPreferences gpu_preferences_;
 
   gles2::TraceOutputter outputter_;
-  std::unique_ptr<ServiceDiscardableManager> discardable_manager_;
   std::unique_ptr<gles2::ShaderTranslatorCache> translator_cache_;
   gles2::FramebufferCompletenessCache completeness_cache_;
   scoped_refptr<gl::GLShareGroup> share_group_;
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc
index 63dd3050..4b7bf17 100644
--- a/gpu/config/gpu_finch_features.cc
+++ b/gpu/config/gpu_finch_features.cc
@@ -429,15 +429,6 @@
 const base::FeatureParam<int> kGPUDriverBugListTestGroupId{
     &kGPUDriverBugListTestGroup, "test_group", 0};
 
-bool UseGles2ForOopR() {
-#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_X86_FAMILY)
-  // GLES3 is not supported on emulators with passthrough. crbug.com/1423712
-  if (gl::UsePassthroughCommandDecoder(base::CommandLine::ForCurrentProcess()))
-    return true;
-#endif
-  return false;
-}
-
 bool IsUsingVulkan() {
 #if BUILDFLAG(IS_ANDROID)
   // Force on if Vulkan feature is enabled from command line.
diff --git a/gpu/config/gpu_finch_features.h b/gpu/config/gpu_finch_features.h
index f0155fb..c2b2a1a 100644
--- a/gpu/config/gpu_finch_features.h
+++ b/gpu/config/gpu_finch_features.h
@@ -136,7 +136,6 @@
 GPU_CONFIG_EXPORT extern const base::FeatureParam<int>
     kGPUDriverBugListTestGroupId;
 
-GPU_CONFIG_EXPORT bool UseGles2ForOopR();
 GPU_CONFIG_EXPORT bool IsUsingVulkan();
 GPU_CONFIG_EXPORT bool IsDrDcEnabled(
     const gpu::GpuFeatureInfo& gpu_feature_info);
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index 54d5473b..6cea802 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -272,7 +272,6 @@
       task_executor_->shader_translator_cache(),
       task_executor_->framebuffer_completeness_cache(), feature_info,
       /*progress_reporter=*/nullptr, task_executor_->gpu_feature_info(),
-      task_executor_->discardable_manager(),
       task_executor_->shared_image_manager());
 
 #if BUILDFLAG(IS_MAC)
diff --git a/gpu/ipc/in_process_command_buffer.h b/gpu/ipc/in_process_command_buffer.h
index 2a12291..3c0f74e4 100644
--- a/gpu/ipc/in_process_command_buffer.h
+++ b/gpu/ipc/in_process_command_buffer.h
@@ -37,7 +37,6 @@
 #include "gpu/command_buffer/service/gpu_task_scheduler_helper.h"
 #include "gpu/command_buffer/service/gr_cache_controller.h"
 #include "gpu/command_buffer/service/program_cache.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/service_transfer_cache.h"
 #include "gpu/command_buffer/service/shared_image_interface_in_process.h"
 #include "gpu/config/gpu_feature_info.h"
diff --git a/gpu/ipc/in_process_gpu_thread_holder.cc b/gpu/ipc/in_process_gpu_thread_holder.cc
index 5107a9cc..4f39c9b 100644
--- a/gpu/ipc/in_process_gpu_thread_holder.cc
+++ b/gpu/ipc/in_process_gpu_thread_holder.cc
@@ -9,10 +9,12 @@
 #include "base/functional/callback_helpers.h"
 #include "base/synchronization/waitable_event.h"
 #include "build/build_config.h"
+#include "gpu/command_buffer/service/feature_info.h"
 #include "gpu/command_buffer/service/scheduler.h"
 #include "gpu/command_buffer/service/service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/sync_point_manager.h"
+#include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_info_collector.h"
 #include "gpu/config/gpu_util.h"
 #include "ui/gl/gl_surface.h"
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.cc b/gpu/ipc/service/gles2_command_buffer_stub.cc
index f1a7fed..4b9aeeb 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.cc
+++ b/gpu/ipc/service/gles2_command_buffer_stub.cc
@@ -92,7 +92,6 @@
       manager->shader_translator_cache(),
       manager->framebuffer_completeness_cache(), feature_info,
       manager->watchdog() /* progress_reporter */, manager->gpu_feature_info(),
-      manager->discardable_manager(),
       manager->shared_image_manager());
 
   // If the `fail_if_major_perf_caveat` context creation attribute was true
diff --git a/gpu/ipc/service/gpu_channel.cc b/gpu/ipc/service/gpu_channel.cc
index b3b0d5d..7fbeaa9 100644
--- a/gpu/ipc/service/gpu_channel.cc
+++ b/gpu/ipc/service/gpu_channel.cc
@@ -705,6 +705,7 @@
     int32_t client_id,
     uint64_t client_tracing_id,
     bool is_gpu_host,
+    bool enable_extra_handles_validation,
     ImageDecodeAcceleratorWorker* image_decode_accelerator_worker,
     const gfx::GpuExtraInfo& gpu_extra_info)
     : gpu_channel_manager_(gpu_channel_manager),
@@ -716,6 +717,7 @@
       io_task_runner_(io_task_runner),
       share_group_(share_group),
       is_gpu_host_(is_gpu_host),
+      enable_extra_handles_validation_(enable_extra_handles_validation),
       filter_(base::MakeRefCounted<GpuChannelMessageFilter>(
           this,
           channel_token,
@@ -725,6 +727,7 @@
           std::move(task_runner))) {
   DCHECK(gpu_channel_manager_);
   DCHECK(client_id_);
+  DCHECK(!(is_gpu_host_ && enable_extra_handles_validation_));
 }
 
 GpuChannel::~GpuChannel() {
@@ -757,13 +760,15 @@
     int32_t client_id,
     uint64_t client_tracing_id,
     bool is_gpu_host,
+    bool enable_extra_handles_validation,
     ImageDecodeAcceleratorWorker* image_decode_accelerator_worker,
     const gfx::GpuExtraInfo& gpu_extra_info) {
   auto gpu_channel = base::WrapUnique(new GpuChannel(
       gpu_channel_manager, channel_token, scheduler, sync_point_manager,
       std::move(share_group), std::move(task_runner), std::move(io_task_runner),
       client_id, client_tracing_id, is_gpu_host,
-      image_decode_accelerator_worker, gpu_extra_info));
+      enable_extra_handles_validation, image_decode_accelerator_worker,
+      gpu_extra_info));
 
   if (!gpu_channel->CreateSharedImageStub(gpu_extra_info)) {
     LOG(ERROR) << "GpuChannel: Failed to create SharedImageStub";
diff --git a/gpu/ipc/service/gpu_channel.h b/gpu/ipc/service/gpu_channel.h
index 7c7c5c0..32f3588 100644
--- a/gpu/ipc/service/gpu_channel.h
+++ b/gpu/ipc/service/gpu_channel.h
@@ -73,6 +73,7 @@
       int32_t client_id,
       uint64_t client_tracing_id,
       bool is_gpu_host,
+      bool enable_extra_handles_validation,
       ImageDecodeAcceleratorWorker* image_decode_accelerator_worker,
       const gfx::GpuExtraInfo& gpu_extra_info);
 
@@ -108,6 +109,9 @@
   }
 
   bool is_gpu_host() const { return is_gpu_host_; }
+  bool enable_extra_handles_validation() const {
+    return enable_extra_handles_validation_;
+  }
 
   // IPC::Listener implementation:
   void OnChannelError() override;
@@ -214,6 +218,7 @@
              int32_t client_id,
              uint64_t client_tracing_id,
              bool is_gpu_host,
+             bool enable_extra_handles_validation,
              ImageDecodeAcceleratorWorker* image_decode_accelerator_worker,
              const gfx::GpuExtraInfo& gpu_extra_info);
 
@@ -263,6 +268,7 @@
   std::unique_ptr<SharedImageStub> shared_image_stub_;
 
   const bool is_gpu_host_;
+  const bool enable_extra_handles_validation_;
 
 #if BUILDFLAG(IS_WIN)
   // Set of active DCOMPTextures.
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc
index 760ca76..7f2d76e 100644
--- a/gpu/ipc/service/gpu_channel_manager.cc
+++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -355,7 +355,6 @@
       shader_translator_cache_(gpu_preferences_),
       default_offscreen_surface_(std::move(default_offscreen_surface)),
       gpu_feature_info_(gpu_feature_info),
-      discardable_manager_(gpu_preferences_),
       image_decode_accelerator_worker_(image_decode_accelerator_worker),
       use_shader_cache_shm_count_(use_shader_cache_shm_count),
       memory_pressure_listener_(
@@ -478,6 +477,7 @@
     int client_id,
     uint64_t client_tracing_id,
     bool is_gpu_host,
+    bool enable_extra_handles_validation,
     const gfx::GpuExtraInfo& gpu_extra_info) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
@@ -496,7 +496,8 @@
   std::unique_ptr<GpuChannel> gpu_channel = GpuChannel::Create(
       this, channel_token, scheduler_, sync_point_manager_, share_group_,
       task_runner_, io_task_runner_, client_id, client_tracing_id, is_gpu_host,
-      image_decode_accelerator_worker_, gpu_extra_info);
+      enable_extra_handles_validation, image_decode_accelerator_worker_,
+      gpu_extra_info);
 
   if (!gpu_channel)
     return nullptr;
@@ -608,7 +609,6 @@
 void GpuChannelManager::LoseAllContexts() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  discardable_manager_.OnContextLost();
   share_group_ = base::MakeRefCounted<gl::GLShareGroup>();
   for (auto& kv : gpu_channels_) {
     kv.second->MarkAllContextsLost();
@@ -829,10 +829,9 @@
   if (program_cache_)
     program_cache_->HandleMemoryPressure(memory_pressure_level);
 
-  // These caches require a current context for cleanup.
+  // SharedContextState requires a current context for cleanup.
   if (shared_context_state_ &&
       shared_context_state_->MakeCurrent(nullptr, true /* needs_gl */)) {
-      discardable_manager_.HandleMemoryPressure(memory_pressure_level);
     shared_context_state_->PurgeMemory(memory_pressure_level);
   }
 
diff --git a/gpu/ipc/service/gpu_channel_manager.h b/gpu/ipc/service/gpu_channel_manager.h
index 45f69cea..7743f06 100644
--- a/gpu/ipc/service/gpu_channel_manager.h
+++ b/gpu/ipc/service/gpu_channel_manager.h
@@ -25,11 +25,12 @@
 #include "build/build_config.h"
 #include "components/viz/common/gpu/vulkan_context_provider.h"
 #include "gpu/command_buffer/common/constants.h"
+#include "gpu/command_buffer/common/context_result.h"
 #include "gpu/command_buffer/common/shm_count.h"
+#include "gpu/command_buffer/service/framebuffer_completeness_cache.h"
 #include "gpu/command_buffer/service/gr_cache_controller.h"
 #include "gpu/command_buffer/service/gr_shader_cache.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
-#include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/service_utils.h"
 #include "gpu/command_buffer/service/shader_translator_cache.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
@@ -116,6 +117,7 @@
                                int client_id,
                                uint64_t client_tracing_id,
                                bool is_gpu_host,
+                               bool enable_extra_handles_validation,
                                const gfx::GpuExtraInfo& gpu_extra_info);
 
   void SetChannelClientPid(int client_id, base::ProcessId client_pid);
@@ -143,9 +145,6 @@
     return gpu_driver_bug_workarounds_;
   }
   const GpuFeatureInfo& gpu_feature_info() const { return gpu_feature_info_; }
-  ServiceDiscardableManager* discardable_manager() {
-    return &discardable_manager_;
-  }
   gles2::Outputter* outputter();
   gles2::ProgramCache* program_cache();
   gles2::ShaderTranslatorCache* shader_translator_cache() {
@@ -329,7 +328,6 @@
   gles2::FramebufferCompletenessCache framebuffer_completeness_cache_;
   scoped_refptr<gl::GLSurface> default_offscreen_surface_;
   GpuFeatureInfo gpu_feature_info_;
-  ServiceDiscardableManager discardable_manager_;
 #if BUILDFLAG(IS_ANDROID)
   // Last time we know the GPU was powered on. Global for tracking across all
   // transport surfaces.
diff --git a/gpu/ipc/service/gpu_channel_manager_unittest.cc b/gpu/ipc/service/gpu_channel_manager_unittest.cc
index 9a1b408..91b6417f 100644
--- a/gpu/ipc/service/gpu_channel_manager_unittest.cc
+++ b/gpu/ipc/service/gpu_channel_manager_unittest.cc
@@ -129,7 +129,7 @@
   ASSERT_TRUE(channel_manager());
   GpuChannel* channel = channel_manager()->EstablishChannel(
       base::UnguessableToken::Create(), kClientId, kClientTracingId, false,
-      gfx::GpuExtraInfo());
+      false, gfx::GpuExtraInfo());
   EXPECT_TRUE(channel);
   EXPECT_EQ(channel_manager()->LookupChannel(kClientId), channel);
 }
diff --git a/gpu/ipc/service/gpu_channel_test_common.cc b/gpu/ipc/service/gpu_channel_test_common.cc
index 59baef3..844d757 100644
--- a/gpu/ipc/service/gpu_channel_test_common.cc
+++ b/gpu/ipc/service/gpu_channel_test_common.cc
@@ -129,7 +129,8 @@
   uint64_t kClientTracingId = 1;
   GpuChannel* channel = channel_manager()->EstablishChannel(
       base::UnguessableToken::Create(), client_id, kClientTracingId,
-      is_gpu_host, gfx::GpuExtraInfo());
+      is_gpu_host, /*enable_extra_handles_validation=*/false,
+      gfx::GpuExtraInfo());
   base::ProcessId kProcessId = 1;
   channel->set_client_pid(kProcessId);
   return channel;
diff --git a/gpu/ipc/service/shared_image_stub.cc b/gpu/ipc/service/shared_image_stub.cc
index 24ecdc7..ee2b3b7 100644
--- a/gpu/ipc/service/shared_image_stub.cc
+++ b/gpu/ipc/service/shared_image_stub.cc
@@ -26,6 +26,7 @@
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/gpu_fence_handle.h"
 #include "ui/gfx/gpu_memory_buffer_handle.h"
+#include "ui/gfx/native_pixmap_handle.h"
 #include "ui/gl/gl_context.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -337,8 +338,32 @@
   TRACE_EVENT2("gpu", "SharedImageStub::OnCreateSharedImageWithBuffer", "width",
                params->si_info->meta.size.width(), "height",
                params->si_info->meta.size.height());
+  gfx::GpuMemoryBufferHandle buffer_handle = std::move(params->buffer_handle);
+
+#if BUILDFLAG(IS_OZONE)
+  if (channel_->enable_extra_handles_validation() &&
+      buffer_handle.type == gfx::NATIVE_PIXMAP) {
+    const auto& pixmap_handle = buffer_handle.native_pixmap_handle();
+    auto format = params->si_info->meta.format;
+    if (!gfx::CanFitImageForSizeAndFormat(
+            pixmap_handle, params->si_info->meta.size, ToBufferFormat(format),
+            /*assume_single_memory_object=*/false)) {
+      LOG(ERROR)
+          << "SharedImageStub: Unable to import buffer, failed validation.";
+      OnError();
+      return;
+    }
+    if (gfx::CloneHandleForIPC(pixmap_handle).planes.empty()) {
+      LOG(ERROR) << "SharedImageStub: Unable to import buffer, failed to dup "
+                    "buffer fds.";
+      OnError();
+      return;
+    }
+  }
+#endif  // BUILDFLAG(IS_OZONE)
+
   if (!CreateSharedImage(
-          params->mailbox, std::move(params->buffer_handle),
+          params->mailbox, std::move(buffer_handle),
           params->si_info->meta.format, params->si_info->meta.size,
           params->si_info->meta.color_space,
           params->si_info->meta.surface_origin,
diff --git a/infra/config/generated/builders/ci/Linux ASan LSan Builder/targets/chromium.memory.json b/infra/config/generated/builders/ci/Linux ASan LSan Builder/targets/chromium.memory.json
index feea8bc..ff5541f62 100644
--- a/infra/config/generated/builders/ci/Linux ASan LSan Builder/targets/chromium.memory.json
+++ b/infra/config/generated/builders/ci/Linux ASan LSan Builder/targets/chromium.memory.json
@@ -913,7 +913,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 77
+          "shards": 84
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
diff --git "a/infra/config/generated/builders/ci/Linux ASan LSan Tests \0501\051/targets/chromium.memory.json" "b/infra/config/generated/builders/ci/Linux ASan LSan Tests \0501\051/targets/chromium.memory.json"
index feea8bc..ff5541f62 100644
--- "a/infra/config/generated/builders/ci/Linux ASan LSan Tests \0501\051/targets/chromium.memory.json"
+++ "b/infra/config/generated/builders/ci/Linux ASan LSan Tests \0501\051/targets/chromium.memory.json"
@@ -913,7 +913,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 77
+          "shards": 84
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
diff --git a/infra/config/generated/builders/ci/Linux Builder/targets/chromium.linux.json b/infra/config/generated/builders/ci/Linux Builder/targets/chromium.linux.json
index 424ddc2..e16af492 100644
--- a/infra/config/generated/builders/ci/Linux Builder/targets/chromium.linux.json
+++ b/infra/config/generated/builders/ci/Linux Builder/targets/chromium.linux.json
@@ -840,7 +840,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
+          "shards": 14
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
diff --git a/infra/config/generated/builders/ci/Linux Tests/targets/chromium.linux.json b/infra/config/generated/builders/ci/Linux Tests/targets/chromium.linux.json
index 4cc80e0..2f317d8 100644
--- a/infra/config/generated/builders/ci/Linux Tests/targets/chromium.linux.json
+++ b/infra/config/generated/builders/ci/Linux Tests/targets/chromium.linux.json
@@ -797,7 +797,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
+          "shards": 14
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
diff --git "a/infra/config/generated/builders/ci/Win x64 Builder \050dbg\051/gn-args.json" "b/infra/config/generated/builders/ci/Win x64 Builder \050dbg\051/gn-args.json"
index 786ccd71..8d2be1b 100644
--- "a/infra/config/generated/builders/ci/Win x64 Builder \050dbg\051/gn-args.json"
+++ "b/infra/config/generated/builders/ci/Win x64 Builder \050dbg\051/gn-args.json"
@@ -4,7 +4,7 @@
     "is_component_build": true,
     "is_debug": true,
     "proprietary_codecs": true,
-    "symbol_level": 1,
+    "symbol_level": 0,
     "target_cpu": "x64",
     "target_os": "win",
     "use_reclient": false,
diff --git a/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json b/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json
index 782730c..d235c60 100644
--- a/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json
+++ b/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json
@@ -1774,7 +1774,7 @@
           },
           "hard_timeout": 7200,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 6
         },
         "test": "updater_tests_system",
         "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
diff --git a/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json b/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json
index 4aae3ed..d88b145 100644
--- a/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json
+++ b/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json
@@ -1745,7 +1745,7 @@
           },
           "hard_timeout": 7200,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 6
         },
         "test": "updater_tests_system",
         "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
diff --git a/infra/config/generated/builders/try/linux-dcheck-off-rel/targets/chromium.linux.json b/infra/config/generated/builders/try/linux-dcheck-off-rel/targets/chromium.linux.json
index 5a764f9..02982bc2 100644
--- a/infra/config/generated/builders/try/linux-dcheck-off-rel/targets/chromium.linux.json
+++ b/infra/config/generated/builders/try/linux-dcheck-off-rel/targets/chromium.linux.json
@@ -840,7 +840,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
+          "shards": 14
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
diff --git a/infra/config/generated/builders/try/linux-full-remote-rel/targets/chromium.linux.json b/infra/config/generated/builders/try/linux-full-remote-rel/targets/chromium.linux.json
index 5a764f9..02982bc2 100644
--- a/infra/config/generated/builders/try/linux-full-remote-rel/targets/chromium.linux.json
+++ b/infra/config/generated/builders/try/linux-full-remote-rel/targets/chromium.linux.json
@@ -840,7 +840,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
+          "shards": 14
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
diff --git a/infra/config/generated/builders/try/linux-rel-test-selection/targets/chromium.linux.json b/infra/config/generated/builders/try/linux-rel-test-selection/targets/chromium.linux.json
index 5a764f9..02982bc2 100644
--- a/infra/config/generated/builders/try/linux-rel-test-selection/targets/chromium.linux.json
+++ b/infra/config/generated/builders/try/linux-rel-test-selection/targets/chromium.linux.json
@@ -840,7 +840,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
+          "shards": 14
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
diff --git a/infra/config/generated/builders/try/linux-rel/targets/chromium.linux.json b/infra/config/generated/builders/try/linux-rel/targets/chromium.linux.json
index 5a764f9..02982bc2 100644
--- a/infra/config/generated/builders/try/linux-rel/targets/chromium.linux.json
+++ b/infra/config/generated/builders/try/linux-rel/targets/chromium.linux.json
@@ -840,7 +840,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
+          "shards": 14
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
diff --git a/infra/config/generated/builders/try/linux_chromium_asan_rel_ng/targets/chromium.memory.json b/infra/config/generated/builders/try/linux_chromium_asan_rel_ng/targets/chromium.memory.json
index feea8bc..ff5541f62 100644
--- a/infra/config/generated/builders/try/linux_chromium_asan_rel_ng/targets/chromium.memory.json
+++ b/infra/config/generated/builders/try/linux_chromium_asan_rel_ng/targets/chromium.memory.json
@@ -913,7 +913,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 77
+          "shards": 84
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
diff --git a/infra/config/generated/builders/try/linux_chromium_compile_rel_ng/targets/chromium.linux.json b/infra/config/generated/builders/try/linux_chromium_compile_rel_ng/targets/chromium.linux.json
index 424ddc2..e16af492 100644
--- a/infra/config/generated/builders/try/linux_chromium_compile_rel_ng/targets/chromium.linux.json
+++ b/infra/config/generated/builders/try/linux_chromium_compile_rel_ng/targets/chromium.linux.json
@@ -840,7 +840,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
+          "shards": 14
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
diff --git a/infra/config/generated/builders/try/win-rel/targets/chromium.win.json b/infra/config/generated/builders/try/win-rel/targets/chromium.win.json
index e7db2c6b..f18a9ad 100644
--- a/infra/config/generated/builders/try/win-rel/targets/chromium.win.json
+++ b/infra/config/generated/builders/try/win-rel/targets/chromium.win.json
@@ -1774,7 +1774,7 @@
           },
           "hard_timeout": 7200,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 6
         },
         "test": "updater_tests_system",
         "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
diff --git a/infra/config/generated/builders/try/win10-dbg/gn-args.json b/infra/config/generated/builders/try/win10-dbg/gn-args.json
index 786ccd71..8d2be1b 100644
--- a/infra/config/generated/builders/try/win10-dbg/gn-args.json
+++ b/infra/config/generated/builders/try/win10-dbg/gn-args.json
@@ -4,7 +4,7 @@
     "is_component_build": true,
     "is_debug": true,
     "proprietary_codecs": true,
-    "symbol_level": 1,
+    "symbol_level": 0,
     "target_cpu": "x64",
     "target_os": "win",
     "use_reclient": false,
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 535b8bf..5e22c48c 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -28718,8 +28718,7 @@
       name: "ToTMacArm64"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
-      dimensions: "cores:12"
-      dimensions: "cpu:x86-64"
+      dimensions: "cpu:arm64"
       dimensions: "free_space:standard"
       dimensions: "os:Mac-15"
       dimensions: "pool:luci.chromium.ci"
@@ -28837,8 +28836,7 @@
       name: "ToTMacArm64PGO"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
-      dimensions: "cores:12"
-      dimensions: "cpu:x86-64"
+      dimensions: "cpu:arm64"
       dimensions: "free_space:standard"
       dimensions: "os:Mac-15"
       dimensions: "pool:luci.chromium.ci"
@@ -29090,8 +29088,7 @@
       name: "ToTMacPGO"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
-      dimensions: "cores:12"
-      dimensions: "cpu:x86-64"
+      dimensions: "cpu:arm64"
       dimensions: "free_space:standard"
       dimensions: "os:Mac-15"
       dimensions: "pool:luci.chromium.ci"
@@ -126210,7 +126207,7 @@
         '  },'
         '  "alerts_enabled": true,'
         '  "cq": "path-based",'
-        '  "fetch_script": "buildtools/reclient_cfgs/fetch_reclient_cfgs.py",'
+        '  "fetch_script": "buildtools/reclient_cfgs/configure_reclient_cfgs.py",'
         '  "rbe_project": ['
         '    {'
         '      "cfg_file": ['
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 9852274..05a6903 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -2269,6 +2269,11 @@
     short_name: "nest-arm"
   }
   builders {
+    name: "buildbucket/luci.chrome.ci/fuchsia-arm64-nest-sd-perf"
+    category: "p/chrome|official"
+    short_name: "nest-arm-perf"
+  }
+  builders {
     name: "buildbucket/luci.chrome.ci/fuchsia-x64-nest-sd"
     category: "p/chrome|official"
     short_name: "nest-x64"
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index d018ee3..8a0c3a3 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -198,6 +198,7 @@
 ) for name, category, short_name in (
     ("fuchsia-arm64-rel-ready", "p/chrome|arm64", "rel-ready"),
     ("fuchsia-arm64-nest-sd", "p/chrome|official", "nest-arm"),
+    ("fuchsia-arm64-nest-sd-perf", "p/chrome|official", "nest-arm-perf"),
     ("fuchsia-ava-astro", "hardware|ava", "ast"),
     ("fuchsia-ava-nelson", "hardware|ava", "nsn"),
     ("fuchsia-ava-sherlock", "hardware|ava", "sher"),
diff --git a/infra/config/subprojects/chromium/ci/chromium.clang.star b/infra/config/subprojects/chromium/ci/chromium.clang.star
index a81806e..0d3d3450 100644
--- a/infra/config/subprojects/chromium/ci/chromium.clang.star
+++ b/infra/config/subprojects/chromium/ci/chromium.clang.star
@@ -95,15 +95,16 @@
     ("clang-tot-device", "iOS|internal", "dev"),
 )]
 
-def tot_mac_builder(*, name, cores = 12, is_rust = False, **kwargs):
+def tot_mac_builder(*, name, is_rust = False, **kwargs):
     if "gn_args" in kwargs:
         kwargs["gn_args"].configs.append("mac")
     desc_tool = "Rust" if is_rust else "Clang"
     return ci.builder(
         name = name,
-        cores = cores,
         os = os.MAC_DEFAULT,
         ssd = True,
+        cores = None,
+        cpu = cpu.ARM64,
         properties = {
             # The Chromium build doesn't need system Xcode, but the ToT
             # bots also build clang and llvm and that build does need system
@@ -1920,8 +1921,6 @@
             "mac_default_x64",
         ],
     ),
-    cores = None,
-    cpu = cpu.ARM64,
     console_view_entry = consoles.console_view_entry(
         category = "ToT Mac",
         short_name = "rel",
@@ -1961,8 +1960,6 @@
             "mac_default_x64",
         ],
     ),
-    cores = None,
-    cpu = cpu.ARM64,
     console_view_entry = consoles.console_view_entry(
         category = "ToT Mac",
         short_name = "dbg",
@@ -2011,8 +2008,6 @@
             "mac_default_x64",
         ],
     ),
-    cores = None,
-    cpu = cpu.ARM64,
     console_view_entry = consoles.console_view_entry(
         category = "ToT Mac",
         short_name = "asn",
@@ -2160,8 +2155,6 @@
             "arm64",
         ],
     ),
-    cores = None,
-    cpu = cpu.ARM64,
     console_view_entry = consoles.console_view_entry(
         category = "ToT Code Coverage",
         short_name = "mac",
@@ -2298,8 +2291,6 @@
             "mac_default_x64",
         ],
     ),
-    cores = None,
-    cpu = cpu.ARM64,
     console_view_entry = consoles.console_view_entry(
         category = "Rust ToT",
         short_name = "mac",
diff --git a/infra/config/subprojects/chromium/ci/chromium.win.star b/infra/config/subprojects/chromium/ci/chromium.win.star
index dbbc9e4a..8f0a3eb 100644
--- a/infra/config/subprojects/chromium/ci/chromium.win.star
+++ b/infra/config/subprojects/chromium/ci/chromium.win.star
@@ -156,6 +156,10 @@
         configs = [
             "gpu_tests",
             "debug_builder",
+            # TODO(https://crbug.com/449751912): Prformance overhead of
+            # symbolizing crash stacks in debug builds were causing renderer
+            # crash related tests to timeout.
+            "no_symbols",
             "remoteexec",
             "win",
             "x64",
diff --git a/infra/config/subprojects/chromium/try/presubmit.star b/infra/config/subprojects/chromium/try/presubmit.star
index 7d514bef..e430f775 100644
--- a/infra/config/subprojects/chromium/try/presubmit.star
+++ b/infra/config/subprojects/chromium/try/presubmit.star
@@ -89,7 +89,7 @@
     name = "reclient-config-deployment-verifier",
     executable = "recipe:reclient_config_deploy_check/tester",
     properties = {
-        "fetch_script": "buildtools/reclient_cfgs/fetch_reclient_cfgs.py",
+        "fetch_script": "buildtools/reclient_cfgs/configure_reclient_cfgs.py",
         "rbe_project": [
             {
                 "name": "rbe-chromium-trusted",
diff --git a/infra/config/targets/autoshard_exceptions.json b/infra/config/targets/autoshard_exceptions.json
index f93f480..e82aa787 100644
--- a/infra/config/targets/autoshard_exceptions.json
+++ b/infra/config/targets/autoshard_exceptions.json
@@ -154,7 +154,7 @@
                 "try_builder": "linux-rel"
             },
             "interactive_ui_tests": {
-                "shards": 12,
+                "shards": 14,
                 "try_builder": "linux-rel"
             },
             "not_site_per_process_blink_wpt_tests": {
@@ -178,7 +178,7 @@
                 "try_builder": "linux_chromium_asan_rel_ng"
             },
             "interactive_ui_tests": {
-                "shards": 77,
+                "shards": 84,
                 "try_builder": "linux_chromium_asan_rel_ng"
             },
             "sync_integration_tests": {
@@ -260,9 +260,9 @@
                 "try_builder": "win-rel"
             },
             "updater_tests_system": {
-                "shards": 5,
+                "shards": 6,
                 "try_builder": "win-rel"
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/internal b/internal
index 54cbda79..828e9cd 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 54cbda79cd53866b327be47ff37f993dde2349d3
+Subproject commit 828e9cd6a5fd8abbc249ac5b3b3df28e3cbcbe7c
diff --git a/ios/chrome/app/application_delegate/app_state.h b/ios/chrome/app/application_delegate/app_state.h
index dd428e38..5ebae01 100644
--- a/ios/chrome/app/application_delegate/app_state.h
+++ b/ios/chrome/app/application_delegate/app_state.h
@@ -56,8 +56,8 @@
 // Container for startup information.
 @property(nonatomic, weak) id<StartupInformation> startupInformation;
 
-// YES if the sign-in upgrade promo has been presented to the user, once.
-@property(nonatomic) BOOL signinUpgradePromoPresentedOnce;
+// YES if the fullscreen sign-in promo has been presented to the user, once.
+@property(nonatomic) BOOL fullscreenSigninPromoPresentedOnce;
 
 // Indicates what action, if any, is taken after a crash (stash tabs, show NTP,
 // show safe mode).
diff --git a/ios/chrome/app/main_application_delegate.mm b/ios/chrome/app/main_application_delegate.mm
index 35d766c..547fe04 100644
--- a/ios/chrome/app/main_application_delegate.mm
+++ b/ios/chrome/app/main_application_delegate.mm
@@ -81,8 +81,10 @@
     _metricsMediator = [[MetricsMediator alloc] init];
     [_mainController setMetricsMediator:_metricsMediator];
     _appState = [[AppState alloc] initWithStartupInformation:_mainController];
-    _pushNotificationDelegate =
-        [[PushNotificationDelegate alloc] initWithAppState:_appState];
+    _pushNotificationDelegate = [[PushNotificationDelegate alloc]
+              initWithAppState:_appState
+        userNotificationCenter:UNUserNotificationCenter
+                                   .currentNotificationCenter];
     [_mainController setAppState:_appState];
   }
   return self;
@@ -148,6 +150,11 @@
   crash_keys::SetCrashedAfterAppWillTerminate();
   base::ios::ScopedCriticalAction::ApplicationWillTerminate();
 
+  UNUserNotificationCenter* center =
+      [UNUserNotificationCenter currentNotificationCenter];
+  center.delegate = nil;
+  _pushNotificationDelegate = nil;
+
   // If `self.didFinishLaunching` is NO, that indicates that the app was
   // terminated before startup could be run. In this situation, skip running
   // shutdown, since the app was never fully started.
diff --git a/ios/chrome/app/resources/Settings.bundle/Experimental.plist b/ios/chrome/app/resources/Settings.bundle/Experimental.plist
index 8fb411c..08851821 100644
--- a/ios/chrome/app/resources/Settings.bundle/Experimental.plist
+++ b/ios/chrome/app/resources/Settings.bundle/Experimental.plist
@@ -180,7 +180,7 @@
 				<string>promos_manager::Promo::PostRestoreDefaultBrowserAlert</string>
 				<string>promos_manager::Promo::DockingPromo</string>
 				<string>promos_manager::Promo::BWGPromo</string>
-				<string>promos_manager::Promo::SigninFullscreen</string>
+				<string>promos_manager::Promo::FullscreenSignin</string>
 			</array>
 			<key>Titles</key>
 			<array>
diff --git a/ios/chrome/app/strings/resources/ios_chromium_strings_iw.xtb b/ios/chrome/app/strings/resources/ios_chromium_strings_iw.xtb
index 63b5f80..787a09b 100644
--- a/ios/chrome/app/strings/resources/ios_chromium_strings_iw.xtb
+++ b/ios/chrome/app/strings/resources/ios_chromium_strings_iw.xtb
@@ -200,6 +200,9 @@
 <translation id="5671188105328420281">‏טיפים ל-Chromium</translation>
 <translation id="5688047395118852662">‏בכרטיס הזה מוצגות הצעות שיעזרו לך ליהנות מהמיטב של Chromium.</translation>
 <translation id="5700709190537129682">‏Chromium לא יכול לבדוק את הסיסמאות שלך</translation>
+<translation id="5731004036450828399">‏כדי לשמור על בטיחות מפתחות הגישה, צריך ליצור קוד אימות לשחזור.
+
+        מפתחות הגישה נשמרים במנהל הסיסמאות, כך שניתן להשתמש בהם ב-Chromium בכל מכשיר.</translation>
 <translation id="584550191241316896">‏נכנסת ל-Chromium</translation>
 <translation id="5862307444128926510">‏ברוכים הבאים ל-Chromium‏</translation>
 <translation id="5889847953983052353">‏כשהאפשרות פועלת:
@@ -267,6 +270,7 @@
 <translation id="7337881442233988129">Chromium</translation>
 <translation id="7357211569052832586">‏הנתונים שבחרת הוסרו מ-Chromium ומהמכשירים המסונכרנים. ייתכן שבכתובת history.google.com יהיה אפשר לגשת לסוגים אחרים של היסטוריית גלישה בחשבון Google, כמו חיפושים ופעילות משירותי Google אחרים.</translation>
 <translation id="7395825497086981028">הסיסמה תישמר במנהל הסיסמאות עבור <ph name="EMAIL" />.</translation>
+<translation id="7434848749522628428">מפתח הגישה יישמר במנהל הסיסמאות בחשבון <ph name="USER_EMAIL" /></translation>
 <translation id="7504829997542308412">‏פתיחת Chromium</translation>
 <translation id="7531461704633548377">‏מ-Chromium</translation>
 <translation id="7554983317097061001">‏טיפ ל-Chromium: כדאי להעביר את Chromium ל-Dock</translation>
@@ -300,6 +304,7 @@
 <translation id="8254729934443216898">‏יש לך הזדמנות לגלות תכונות חדשות שיעזרו לך להפיק את המירב מ-Chromium.</translation>
 <translation id="8293572045658095405">‏במצב פרטי ב-Chromium</translation>
 <translation id="830951810931292870">‏פתיחת כתובות ה-URL שהוזנו ב-Chromium במצב פרטי.</translation>
+<translation id="8385503371191508165">כדאי להתחיל לשמור מפתחות גישה באמצעות מנהל הסיסמאות</translation>
 <translation id="8390323835464055390">‏רוצה להיכנס לחשבון באמצעות הסיסמאות ומפתחות הגישה השמורים שלך מכל אפליקציה באמצעות Chromium למילוי אוטומטי?</translation>
 <translation id="8409374867500149834">‏האבטחה החזקה ביותר של Chromium מגינה עליך מפני אתרים מזיקים</translation>
 <translation id="8543509361021925846">{THRESHOLD,plural, =1{‏הפעולה הזו מתבצעת כשלא משתמשים ב-Chromium במשך דקה ({THRESHOLD}). נתונים שנשמרו רק במכשיר הזה בזמן שהיית מחובר לחשבון יימחקו. הנתונים האלה יכולים לכלול היסטוריה וסיסמאות.}one{‏הפעולה הזו מתבצעת כשלא משתמשים ב-Chromium במשך {THRESHOLD} דקות. נתונים שנשמרו רק במכשיר הזה בזמן שהיית מחובר לחשבון יימחקו. הנתונים האלה יכולים לכלול היסטוריה וסיסמאות.}two{‏הפעולה הזו מתבצעת כשלא משתמשים ב-Chromium במשך {THRESHOLD} דקות. נתונים שנשמרו רק במכשיר הזה בזמן שהיית מחובר לחשבון יימחקו. הנתונים האלה יכולים לכלול היסטוריה וסיסמאות.}other{‏הפעולה הזו מתבצעת כשלא משתמשים ב-Chromium במשך {THRESHOLD} דקות. נתונים שנשמרו רק במכשיר הזה בזמן שהיית מחובר לחשבון יימחקו. הנתונים האלה יכולים לכלול היסטוריה וסיסמאות.}}</translation>
diff --git a/ios/chrome/app/strings/resources/ios_chromium_strings_ms.xtb b/ios/chrome/app/strings/resources/ios_chromium_strings_ms.xtb
index 0f37dc9..19acb42 100644
--- a/ios/chrome/app/strings/resources/ios_chromium_strings_ms.xtb
+++ b/ios/chrome/app/strings/resources/ios_chromium_strings_ms.xtb
@@ -200,6 +200,9 @@
 <translation id="5671188105328420281">Petua Chromium</translation>
 <translation id="5688047395118852662">Kad ini menunjukkan cadangan tentang cara memanfaatkan Chromium sepenuhnya.</translation>
 <translation id="5700709190537129682">Chromium tidak dapat menyemak kata laluan anda</translation>
+<translation id="5731004036450828399">Untuk memastikan kunci laluan anda selamat, anda perlu membuat PIN pemulihan dahulu.
+
+        Kunci laluan disimpan dalam Password Manager, yang membolehkan anda menggunakan kunci laluan tersebut dalam Chromium pada mana-mana peranti.</translation>
 <translation id="584550191241316896">Log masuk ke Chromium</translation>
 <translation id="5862307444128926510">Selamat datang ke Chromium</translation>
 <translation id="5889847953983052353">Apabila dihidupkan:
@@ -267,6 +270,7 @@
 <translation id="7337881442233988129">Chromium</translation>
 <translation id="7357211569052832586">Data yang dipilih telah dialih keluar daripada Chromium dan peranti yang disegerakkan. Google Account anda mungkin mempunyai sejarah penyemakan imbas dalam bentuk lain seperti carian dan aktiviti daripada perkhidmatan Google yang lain di history.google.com.</translation>
 <translation id="7395825497086981028">Kata laluan anda akan disimpan pada Pengurus Kata Laluan untuk <ph name="EMAIL" />.</translation>
+<translation id="7434848749522628428">Kunci laluan ini akan disimpan pada Password Manager untuk <ph name="USER_EMAIL" /></translation>
 <translation id="7504829997542308412">Buka Chromium</translation>
 <translation id="7531461704633548377">Daripada Chromium</translation>
 <translation id="7554983317097061001">Petua Chromium: Alihkan Chromium kepada dok</translation>
@@ -300,6 +304,7 @@
 <translation id="8254729934443216898">Terokai ciri baharu yang membantu anda memanfaatkan Chromium sepenuhnya.</translation>
 <translation id="8293572045658095405">Anda dalam Inkognito Chromium</translation>
 <translation id="830951810931292870">Buka URL yang dimasukkan pada Chromium dalam Inkognito.</translation>
+<translation id="8385503371191508165">Mula menyimpan kunci laluan dengan Password Manager</translation>
 <translation id="8390323835464055390">Log masuk dengan kata laluan &amp; kunci laluan anda yang disimpan daripada mana-mana apl menggunakan Chromium untuk Autolengkap</translation>
 <translation id="8409374867500149834">Anda mempunyai keselamatan terkuat Chromium terhadap laman web berbahaya</translation>
 <translation id="8543509361021925846">{THRESHOLD,plural, =1{Perkara ini berlaku apabila Chromium tidak digunakan selama {THRESHOLD} minit. Data yang disimpan hanya pada peranti ini semasa anda log masuk akan dipadamkan. Data ini boleh termasuk sejarah dan kata laluan.}other{Perkara ini berlaku apabila Chromium tidak digunakan selama {THRESHOLD} minit. Data yang disimpan hanya pada peranti ini semasa anda log masuk akan dipadamkan. Data ini boleh termasuk sejarah dan kata laluan.}}</translation>
diff --git a/ios/chrome/app/strings/resources/ios_chromium_strings_ur.xtb b/ios/chrome/app/strings/resources/ios_chromium_strings_ur.xtb
index ddbdaeec..6b5645bc 100644
--- a/ios/chrome/app/strings/resources/ios_chromium_strings_ur.xtb
+++ b/ios/chrome/app/strings/resources/ios_chromium_strings_ur.xtb
@@ -200,6 +200,9 @@
 <translation id="5671188105328420281">‏Chromium کی تجاویز</translation>
 <translation id="5688047395118852662">‏یہ کارڈ آپ کو Chromium سے زیادہ سے زیادہ فائدہ اٹھانے کے بارے میں تجاویز دکھاتا ہے۔</translation>
 <translation id="5700709190537129682">‏Chromium آپ کے پاس ورڈز چیک نہیں کر سکتا ہے</translation>
+<translation id="5731004036450828399">‏اپنی پاس کیز کو محفوظ رکھنے کے لیے، آپ کو پہلے ایک بازیابی PIN بنانے کی ضرورت ہوگی۔
+
+        پاس کیز کو پاس ورڈ مینیجر میں محفوظ کیا جاتا ہے، جو آپ کو انہیں کسی بھی آلہ پر Chromium میں استعمال کرنے دیتا ہے۔</translation>
 <translation id="584550191241316896">‏Chromium میں سائن ان ہوا</translation>
 <translation id="5862307444128926510">‏Chromium میں خوش آمدید</translation>
 <translation id="5889847953983052353">‏آن ہونے پر:
@@ -267,6 +270,7 @@
 <translation id="7337881442233988129">Chromium</translation>
 <translation id="7357211569052832586">‏Chromium اور مطابقت پذیر آلات سے منتخب ڈیٹا کو ہٹا دیا گیا ہے۔ ممکن ہے کہ history.google.com پر آپ کے Google اکاؤنٹ میں دیگر Google سروسز سے تلاشیں اور سرگرمی جیسی براؤزنگ کی سرگزشت کی دیگر شکلیں موجود ہوں۔</translation>
 <translation id="7395825497086981028">آپ کا پاس ورڈ <ph name="EMAIL" /> کے لیے پاس ورڈ مینیجر میں محفوظ کیا جائے گا۔</translation>
+<translation id="7434848749522628428">یہ پاس کی <ph name="USER_EMAIL" /> کے پاس ورڈ مینیجر میں محفوظ کی جائے گی</translation>
 <translation id="7504829997542308412">‏‫Chromium کھولیں</translation>
 <translation id="7531461704633548377">‏‫Chromium سے</translation>
 <translation id="7554983317097061001">‏‫Chromium کی تجویز: ڈاک پر Chromium کو منتقل کریں</translation>
@@ -300,6 +304,7 @@
 <translation id="8254729934443216898">‏نئی خصوصیات دریافت کریں جو آپ کی Chromium سے زیادہ سے زیادہ فائدہ اٹھانے میں مدد کرتی ہیں۔</translation>
 <translation id="8293572045658095405">‏آپ Chromium پوشیدگی میں ہیں</translation>
 <translation id="830951810931292870">‏درج کردہ URLs کو Chromium میں پوشیدگی میں کھولتا ہے۔</translation>
+<translation id="8385503371191508165">پاس ورڈ مینیجر کے ساتھ پاس کیز محفوظ کرنا شروع کریں</translation>
 <translation id="8390323835464055390">‏‫Chromium برائے آٹو فل کا استعمال کرتے ہوئے کسی بھی ایپ کے محفوظ کردہ پاس ورڈز اور پاس کیز کی مدد سے سائن ان کریں</translation>
 <translation id="8409374867500149834">‏آپ کے پاس نقصان دہ ویب سائٹس کے خلاف Chromium کی مضبوط ترین سیکیورٹی ہے</translation>
 <translation id="8543509361021925846">{THRESHOLD,plural, =1{‏ایسا اس وقت ہوتا ہے جب Chromium {THRESHOLD} منٹ تک استعمال نہیں ہوتا ہے۔ آپ کے سائن ہونے کے دوران صرف اس آلے پر محفوظ کردہ ڈیٹا کو حذف کر دیا جائے گا۔ اس میں سرگزشت اور پاس ورڈز شامل ہو سکتے ہیں۔}other{‏ایسا اس وقت ہوتا ہے جب Chromium {THRESHOLD} منٹ تک استعمال نہیں ہوتا ہے۔ آپ کے سائن ہونے کے دوران صرف اس آلے پر محفوظ کردہ ڈیٹا کو حذف کر دیا جائے گا۔ اس میں سرگزشت اور پاس ورڈز شامل ہو سکتے ہیں۔}}</translation>
diff --git a/ios/chrome/app/strings/resources/ios_google_chrome_strings_iw.xtb b/ios/chrome/app/strings/resources/ios_google_chrome_strings_iw.xtb
index 7e16ab2..bf29989 100644
--- a/ios/chrome/app/strings/resources/ios_google_chrome_strings_iw.xtb
+++ b/ios/chrome/app/strings/resources/ios_google_chrome_strings_iw.xtb
@@ -87,6 +87,9 @@
 <translation id="2940565985148833945">‏טיפ ל-Chrome: חיפוש באמצעות Lens</translation>
 <translation id="2957447865124070833">‏בוחרים באפשרות <ph name="BEGIN_BOLD" />Chrome<ph name="END_BOLD" />.</translation>
 <translation id="2986402782150919988">‏הוספה של סימניות אל Chrome</translation>
+<translation id="3027236223714901838">‏כדי לשמור על בטיחות מפתחות הגישה שלך, צריך ליצור קוד אימות לשחזור.
+
+        מפתחות הגישה נשמרים במנהל הסיסמאות של Google, כך שניתן להשתמש בהם ב-Chrome בכל מכשיר.</translation>
 <translation id="3030414234702425231">‏יציאה מחשבון שמנוהל על ידי <ph name="SIGNOUT_MANAGED_DOMAIN" /> תגרום למחיקת נתוני Chrome שלך מהמכשיר הזה. הנתונים האלה יישארו בחשבון Google שלך.</translation>
 <translation id="3042447686908081308">‏ייבוא ל-Chrome</translation>
 <translation id="3051521853355903257">‏ניהול הסיסמאות ב-Chrome</translation>
diff --git a/ios/chrome/app/strings/resources/ios_google_chrome_strings_ms.xtb b/ios/chrome/app/strings/resources/ios_google_chrome_strings_ms.xtb
index 78132df..71becf9 100644
--- a/ios/chrome/app/strings/resources/ios_google_chrome_strings_ms.xtb
+++ b/ios/chrome/app/strings/resources/ios_google_chrome_strings_ms.xtb
@@ -87,6 +87,9 @@
 <translation id="2940565985148833945">Petua Chrome: Buat carian dengan Lens</translation>
 <translation id="2957447865124070833">Pilih <ph name="BEGIN_BOLD" />Chrome<ph name="END_BOLD" /></translation>
 <translation id="2986402782150919988">Tambahkan penanda halaman pada Chrome</translation>
+<translation id="3027236223714901838">Untuk memastikan kunci laluan anda selamat, anda perlu membuat PIN pemulihan dahulu.
+
+        Kunci laluan disimpan dalam Google Password Manager, yang membolehkan anda menggunakan kunci laluan tersebut dalam Chrome pada mana-mana peranti.</translation>
 <translation id="3030414234702425231">Oleh sebab anda log keluar daripada akaun yang diurus oleh <ph name="SIGNOUT_MANAGED_DOMAIN" />, data Chrome anda akan dipadamkan daripada peranti ini. Data anda akan kekal dalam Google Account anda.</translation>
 <translation id="3042447686908081308">Import kepada Chrome</translation>
 <translation id="3051521853355903257">Urus kata laluan dalam Chrome</translation>
diff --git a/ios/chrome/app/strings/resources/ios_google_chrome_strings_ur.xtb b/ios/chrome/app/strings/resources/ios_google_chrome_strings_ur.xtb
index a9979f2..f5a2b9e 100644
--- a/ios/chrome/app/strings/resources/ios_google_chrome_strings_ur.xtb
+++ b/ios/chrome/app/strings/resources/ios_google_chrome_strings_ur.xtb
@@ -87,6 +87,9 @@
 <translation id="2940565985148833945">‏‫Chrome تجویز: لینز کے ساتھ تلاش کریں</translation>
 <translation id="2957447865124070833">‏<ph name="BEGIN_BOLD" />Chrome<ph name="END_BOLD" /> منتخب کریں</translation>
 <translation id="2986402782150919988">‏‫Chrome میں بُک مارکس شامل کریں</translation>
+<translation id="3027236223714901838">‏اپنی پاس کیز کو محفوظ رکھنے کے لیے، آپ کو پہلے ایک بازیابی PIN بنانے کی ضرورت ہوگی۔
+
+        پاس کیز کو Google پاس ورڈ مینیجر میں محفوظ کیا جاتا ہے، جو آپ کو انہیں کسی بھی آلہ پر Chrome میں استعمال کرنے دیتا ہے۔</translation>
 <translation id="3030414234702425231">‏چونکہ آپ <ph name="SIGNOUT_MANAGED_DOMAIN" /> کے زیر انتظام ایک اکاؤنٹ سے سائن آؤٹ کر رہے ہیں، لہذا آپ کا Chrome ڈیٹا اس آلہ سے حذف ہو جائے گا۔ آپ کا ڈیٹا آپ کے Google اکاؤنٹ میں رہے گا۔</translation>
 <translation id="3042447686908081308">‏‫Chrome پر درآمد کریں</translation>
 <translation id="3051521853355903257">‏‫Chrome میں پاس ورڈز کا نظم کریں</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_ar.xtb b/ios/chrome/app/strings/resources/ios_strings_ar.xtb
index 3c745a0c..b71a6a4 100644
--- a/ios/chrome/app/strings/resources/ios_strings_ar.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_ar.xtb
@@ -1046,6 +1046,7 @@
 <translation id="5059136629401106827">حسنًا</translation>
 <translation id="5062321486222145940">‏تثبيت Google Drive</translation>
 <translation id="5067540696062462445">عمليات التنزيل</translation>
+<translation id="5070314445039955096">بعد مغادرة الموقع الإلكتروني "<ph name="SITE_DOMAIN" />"، سيتم إيقاف جميع أذوناته.</translation>
 <translation id="507187271399539513">{count,plural, =1{‫{position} من إجمالي طريقة دفع واحدة ({count})}zero{‫{position} من إجمالي {count} طريقة دفع}two{‫{position} من إجمالي طريقتَي دفع ({count})}few{‫{position} من إجمالي {count} طُرق دفع}many{‫{position} من إجمالي {count} طريقة دفع}other{‫{position} من إجمالي {count} طريقة دفع}}</translation>
 <translation id="5075939510584558547">الحفظ بدون تشفير</translation>
 <translation id="5076534614343816664">تتبُّع الأسعار</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_as.xtb b/ios/chrome/app/strings/resources/ios_strings_as.xtb
index 128bb2e..d135bdf 100644
--- a/ios/chrome/app/strings/resources/ios_strings_as.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_as.xtb
@@ -1046,6 +1046,7 @@
 <translation id="5059136629401106827">ঠিক আছে</translation>
 <translation id="5062321486222145940">Google Drive ইনষ্টল কৰক</translation>
 <translation id="5067540696062462445">ডাউনল’ড</translation>
+<translation id="5070314445039955096">আপুনি এই ছাইটটোৰ পৰা বাহিৰ ওলালে "<ph name="SITE_DOMAIN" />"ৰ অনুমতিসমূহ অফ হিচাপে ৰিছেট কৰা হ’ব</translation>
 <translation id="507187271399539513">{count,plural, =1{{count} টা পৰিশোধ পদ্ধতিৰ {position} নম্বৰ পদ্ধতিটো}one{{count} টা পৰিশোধ পদ্ধতিৰ {position} নম্বৰ পদ্ধতিটো}other{{count} টা পৰিশোধ পদ্ধতিৰ {position} নম্বৰ পদ্ধতিটো}}</translation>
 <translation id="5075939510584558547">এনক্ৰিপশ্বনৰ অবিহনে ছেভ কৰক</translation>
 <translation id="5076534614343816664">মূল্য ট্ৰেকিং</translation>
@@ -1678,6 +1679,7 @@
 <translation id="7437712592381799950">আপোনাৰ বাবে প্ৰস্তুত কৰা অধিক সমল চাবলৈ আপুনি ওপৰলৈ ছোৱাইপ কৰিব পাৰে।</translation>
 <translation id="7438473148803748807">সকলোৱে লগে লগে “<ph name="GROUP_NAME" />" টেবৰ গোটৰ এক্সেছ হেৰুৱাব আৰু আটাইবোৰ আমন্ত্ৰণৰ লিংক অফ কৰা হ’ব</translation>
 <translation id="7438481509621345350">আপুনি এতিয়া এটা নতুন টেব খুলিলে <ph name="CHANNEL_NAME" />ৰ পৰা কাহিনী দেখা পাব।</translation>
+<translation id="7445682342344043969">পপ-আপ অৱৰোধ কৰা হৈছে</translation>
 <translation id="7451023311965289370">{count,plural, =1{বুকমাৰ্ক "{title}"ত ছেভ কৰা হৈছে}one{বুকমাৰ্ক "{title}"ত ছেভ কৰা হৈছে}other{বুকমাৰ্ক "{title}"ত ছেভ কৰা হৈছে}}</translation>
 <translation id="7451404862887877229">প্ৰথমে "গোপনীয়তা আৰু সুৰক্ষা"ত টিপক আৰু তাৰ পাছত "সুৰক্ষিত ব্ৰাউজিঙ"ত টিপক</translation>
 <translation id="7452393099119876789">আপুনি এইটো মাত্ৰ এবাৰহে কৰিব লাগিব।</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_fa.xtb b/ios/chrome/app/strings/resources/ios_strings_fa.xtb
index edf4af63..d4913ed 100644
--- a/ios/chrome/app/strings/resources/ios_strings_fa.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_fa.xtb
@@ -1046,6 +1046,7 @@
 <translation id="5059136629401106827">خوب</translation>
 <translation id="5062321486222145940">‏نصب Google Drive</translation>
 <translation id="5067540696062462445">بارگیری‌ها</translation>
+<translation id="5070314445039955096">هرگاه این سایت را ترک کنید، اجازه‌های «<ph name="SITE_DOMAIN" />» به حالت خاموش بازنشانی می‌شوند</translation>
 <translation id="507187271399539513">{count,plural, =1{‫{position} از {count} روش پرداخت}one{‫{position} از {count} روش پرداخت}other{‫{position} از {count} روش پرداخت}}</translation>
 <translation id="5075939510584558547">ذخیره بدون رمزگذاری</translation>
 <translation id="5076534614343816664">پیگیری قیمت</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_ja.xtb b/ios/chrome/app/strings/resources/ios_strings_ja.xtb
index f6d4d61..538db95 100644
--- a/ios/chrome/app/strings/resources/ios_strings_ja.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_ja.xtb
@@ -1246,7 +1246,7 @@
 <translation id="5803566855766646066">この新しいカードを破棄してもよろしいですか?</translation>
 <translation id="5804241973901381774">権限</translation>
 <translation id="58081802114142635">Google AI を改善するため、ユーザーのアクティビティが使用されます</translation>
-<translation id="5813223718099710537">この機能を使用すると、ページ コンテンツと URL が Google に送信されます。この動作は設定でいつでもオフにできます。</translation>
+<translation id="5813223718099710537">この機能を使用すると、ページ コンテンツと URL が Google に送信されます。この動作は設定でいつでもオフにできます。</translation>
 <translation id="5817176759448082654">パスワード チェックアップ</translation>
 <translation id="5818161931099046557">Google アカウントで編集</translation>
 <translation id="5819208479324046259"><ph name="MANAGER" /> によって管理されています。<ph name="BEGIN_LINK" />詳細<ph name="END_LINK" /></translation>
@@ -1667,7 +1667,7 @@
 <translation id="7403227077090127147"><ph name="USER_NAME" />(<ph name="USER_EMAIL" />)さんは「<ph name="GROUP_NAME" />」タブグループに直ちにアクセスできなくなります</translation>
 <translation id="7409985198648820906"><ph name="UNREAD_COUNT" /> 件の未読記事があります。</translation>
 <translation id="7412027924265291969">続行</translation>
-<translation id="741204030948306876">ON にする</translation>
+<translation id="741204030948306876">オンにする</translation>
 <translation id="7417689656810783109">グループ名を変更</translation>
 <translation id="7419565702166471774">常に安全な接続を使用する</translation>
 <translation id="7425053386765766120">お支払い方法を追加…</translation>
@@ -1705,7 +1705,7 @@
 <translation id="7512909159512473473">パスフレーズを入力…</translation>
 <translation id="7514365320538308">ダウンロード</translation>
 <translation id="7514530647142300459">Google アカウント(<ph name="EMAIL" />)のパスワードを引き続き使用してください</translation>
-<translation id="7522587739845660862">位置情報についての詳細</translation>
+<translation id="7522587739845660862">ユーザーの位置に関する情報</translation>
 <translation id="7522948500476882302">ファイルのダウンロード時に、1 か月後に自動削除するかどうかを選択できます</translation>
 <translation id="7524055474074101597">アプリからパスワードなどにアクセスできます。</translation>
 <translation id="75362970626182391">新しいタブグループ</translation>
@@ -1722,7 +1722,7 @@
 <translation id="7582666837709186351">カメラを使って、写真や周囲のテキストを翻訳できます。</translation>
 <translation id="7582857256643797524">この設定はデバイスで有効になっています</translation>
 <translation id="7583004045319035904"><ph name="BIOMETRIC_AUTHENITCATION_TYPE" /> を使用して、シークレット タブのロックを解除します。</translation>
-<translation id="7588149553568293500">お客様が選択できる項目の詳細をご確認ください。</translation>
+<translation id="7588149553568293500">プライバシーの設定についてご確認ください。</translation>
 <translation id="7596103009408415410">{COUNT,plural, =1{アクティブでない {COUNT} 個のタブを閉じますか?}other{アクティブでない {COUNT} 個のタブを閉じますか?}}</translation>
 <translation id="7598439272585186139">セキュリティを強化</translation>
 <translation id="7600965453749440009"><ph name="LANGUAGE" />を翻訳しない</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_ka.xtb b/ios/chrome/app/strings/resources/ios_strings_ka.xtb
index 8bb175f..c506ee8 100644
--- a/ios/chrome/app/strings/resources/ios_strings_ka.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_ka.xtb
@@ -1046,6 +1046,7 @@
 <translation id="5059136629401106827">კარგი</translation>
 <translation id="5062321486222145940">Google Drive-ის ინსტალაცია</translation>
 <translation id="5067540696062462445">ჩამოტვირთვები</translation>
+<translation id="5070314445039955096">საიტის (<ph name="SITE_DOMAIN" />) ნებართვები გადაყენდება და გახდება „გამორთული“, როცა ამ საიტს დატოვებთ</translation>
 <translation id="507187271399539513">{count,plural, =1{{count}-დან {position} გადახდის მეთოდი}other{{count}-დან {position} გადახდის მეთოდი}}</translation>
 <translation id="5075939510584558547">დაშიფვრის გარეშე შენახვა</translation>
 <translation id="5076534614343816664">ფასისთვის თვალის მიდევნება</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_mn.xtb b/ios/chrome/app/strings/resources/ios_strings_mn.xtb
index ea664aa..31a990f02 100644
--- a/ios/chrome/app/strings/resources/ios_strings_mn.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_mn.xtb
@@ -1046,6 +1046,7 @@
 <translation id="5059136629401106827">OK</translation>
 <translation id="5062321486222145940">Google Drive суулгах</translation>
 <translation id="5067540696062462445">Татаж авсан файл</translation>
+<translation id="5070314445039955096">Таныг энэ сайтаас гарах үед "<ph name="SITE_DOMAIN" />"-н зөвшөөрлийг унтраасан болгож шинэчилнэ</translation>
 <translation id="507187271399539513">{count,plural, =1{{count}-н {position}-р төлбөрийн хэрэгсэл}other{{count}-н {position}-р төлбөрийн хэрэгсэл}}</translation>
 <translation id="5075939510584558547">Шифрлэлтгүйгээр хадгалах</translation>
 <translation id="5076534614343816664">Үнэ мөшгөх</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_vi.xtb b/ios/chrome/app/strings/resources/ios_strings_vi.xtb
index 994ebef..a106a77 100644
--- a/ios/chrome/app/strings/resources/ios_strings_vi.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_vi.xtb
@@ -1046,6 +1046,7 @@
 <translation id="5059136629401106827">OK</translation>
 <translation id="5062321486222145940">Cài đặt Google Drive</translation>
 <translation id="5067540696062462445">Tệp đã tải xuống</translation>
+<translation id="5070314445039955096">Các quyền của "<ph name="SITE_DOMAIN" />" sẽ được đặt lại thành tắt khi bạn rời khỏi trang web này</translation>
 <translation id="507187271399539513">{count,plural, =1{Phương thức thanh toán {position}/{count}}other{Phương thức thanh toán {position}/{count}}}</translation>
 <translation id="5075939510584558547">Lưu mà không mã hoá</translation>
 <translation id="5076534614343816664">Theo dõi giá</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_zh-HK.xtb b/ios/chrome/app/strings/resources/ios_strings_zh-HK.xtb
index 8117cbe..856d8ec4 100644
--- a/ios/chrome/app/strings/resources/ios_strings_zh-HK.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_zh-HK.xtb
@@ -1046,6 +1046,7 @@
 <translation id="5059136629401106827">確定</translation>
 <translation id="5062321486222145940">安裝「Google 雲端硬碟」</translation>
 <translation id="5067540696062462445">下載內容</translation>
+<translation id="5070314445039955096">「<ph name="SITE_DOMAIN" />」的權限會在你離開此網站後重設為停用</translation>
 <translation id="507187271399539513">{count,plural, =1{第 {position} 個付款方法,總共有 {count} 個}other{第 {position} 個付款方法,總共有 {count} 個}}</translation>
 <translation id="5075939510584558547">不加密直接儲存</translation>
 <translation id="5076534614343816664">價格追蹤</translation>
diff --git a/ios/chrome/browser/account_picker/ui_bundled/account_picker_selection/account_picker_selection_screen_table_view_controller_model_delegate.h b/ios/chrome/browser/account_picker/ui_bundled/account_picker_selection/account_picker_selection_screen_table_view_controller_model_delegate.h
index cfad337..05ed57b 100644
--- a/ios/chrome/browser/account_picker/ui_bundled/account_picker_selection/account_picker_selection_screen_table_view_controller_model_delegate.h
+++ b/ios/chrome/browser/account_picker/ui_bundled/account_picker_selection/account_picker_selection_screen_table_view_controller_model_delegate.h
@@ -7,6 +7,7 @@
 
 #import <Foundation/Foundation.h>
 
+@class AccountPickerSelectionScreenIdentityItemConfigurator;
 @class AccountPickerSelectionScreenViewController;
 
 // Protocol to get the model.
@@ -14,7 +15,9 @@
     AccountPickerSelectionScreenTableViewControllerModelDelegate <NSObject>
 
 // Returns all the configurators to generate model items.
-@property(nonatomic, strong, readonly) NSArray* sortedIdentityItemConfigurators;
+@property(nonatomic, strong, readonly)
+    NSArray<AccountPickerSelectionScreenIdentityItemConfigurator*>*
+        sortedIdentityItemConfigurators;
 
 @end
 
diff --git a/ios/chrome/browser/account_picker/ui_bundled/account_picker_selection/account_picker_selection_screen_view_controller.mm b/ios/chrome/browser/account_picker/ui_bundled/account_picker_selection/account_picker_selection_screen_view_controller.mm
index 1422506..d569472 100644
--- a/ios/chrome/browser/account_picker/ui_bundled/account_picker_selection/account_picker_selection_screen_view_controller.mm
+++ b/ios/chrome/browser/account_picker/ui_bundled/account_picker_selection/account_picker_selection_screen_view_controller.mm
@@ -44,7 +44,7 @@
     [subView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
     [subView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
   ]];
-  [self didMoveToParentViewController:self.tableViewController];
+  [self.tableViewController didMoveToParentViewController:self];
 }
 
 - (void)viewDidLayoutSubviews {
diff --git a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_container_coordinator.mm b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_container_coordinator.mm
index cb44548b..03c8b44 100644
--- a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_container_coordinator.mm
+++ b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_container_coordinator.mm
@@ -6,15 +6,20 @@
 
 #import "ios/chrome/browser/aim/prototype/coordinator/aim_prototype_coordinator.h"
 #import "ios/chrome/browser/aim/prototype/coordinator/aim_prototype_entrypoint.h"
+#import "ios/chrome/browser/aim/prototype/coordinator/aim_prototype_navigation_mediator.h"
 #import "ios/chrome/browser/aim/prototype/ui/aim_prototype_container_view_controller.h"
 #import "ios/chrome/browser/aim/prototype/ui/aim_prototype_dismiss_animator.h"
 #import "ios/chrome/browser/aim/prototype/ui/aim_prototype_present_animator.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
+#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/public/commands/browser_coordinator_commands.h"
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
+#import "ios/chrome/browser/url_loading/model/url_loading_browser_agent.h"
+#import "ios/web/public/web_state.h"
 
 @interface AIMPrototypeContainerCoordinator () <
     AIMPrototypeContainerViewControllerDelegate,
+    AIMPrototypeNavigationMediatorDelegate,
     UIViewControllerTransitioningDelegate>
 
 @end
@@ -22,6 +27,8 @@
 @implementation AIMPrototypeContainerCoordinator {
   // The coordinator for the main AIM UI.
   AIMPrototypeCoordinator* _aimCoordinator;
+  // The mediator for the web navigation.
+  AIMPrototypeNavigationMediator* _navigationMediator;
   // The entrypoint that triggered the AIM prototype.
   AIMPrototypeEntrypoint _entrypoint;
   // An optional query to pre-fill the omnibox.
@@ -48,11 +55,22 @@
   _viewController.transitioningDelegate = self;
   _viewController.delegate = self;
 
+  UrlLoadingBrowserAgent* urlLoadingBrowserAgent =
+      UrlLoadingBrowserAgent::FromBrowser(self.browser);
+  web::WebState::CreateParams params =
+      web::WebState::CreateParams(self.profile);
+  _navigationMediator = [[AIMPrototypeNavigationMediator alloc]
+      initWithUrlLoadingBrowserAgent:urlLoadingBrowserAgent
+                      webStateParams:params];
+  _navigationMediator.consumer = _viewController;
+  _navigationMediator.delegate = self;
+
   _aimCoordinator = [[AIMPrototypeCoordinator alloc]
       initWithBaseViewController:self.baseViewController
                          browser:self.browser
                       entrypoint:_entrypoint
-                           query:_query];
+                           query:_query
+                       URLLoader:_navigationMediator];
   _aimCoordinator.omniboxPopupPresenterDelegate = _viewController;
   [_aimCoordinator start];
 
@@ -70,6 +88,9 @@
 
   [_aimCoordinator stop];
   _aimCoordinator = nil;
+
+  [_navigationMediator disconnect];
+  _navigationMediator = nil;
 }
 
 #pragma mark - UIViewControllerTransitioningDelegate
@@ -94,6 +115,20 @@
 
 - (void)aimPrototypeContainerViewControllerDidTapCloseButton:
     (AIMPrototypeViewController*)viewController {
+  [self dismissAIMPrototype];
+}
+
+#pragma mark - AIMPrototypeNavigationMediatorDelegate
+
+- (void)navigationMediatorDidFinish:
+    (AIMPrototypeNavigationMediator*)navigationMediator {
+  [self dismissAIMPrototype];
+}
+
+#pragma mark - Private
+
+// Sends the command to get the AIM prototype dismissed.
+- (void)dismissAIMPrototype {
   id<BrowserCoordinatorCommands> commands = HandlerForProtocol(
       self.browser->GetCommandDispatcher(), BrowserCoordinatorCommands);
   [commands hideAIMPrototype];
diff --git a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_coordinator.h b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_coordinator.h
index 493b337..b6ceb15 100644
--- a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_coordinator.h
+++ b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_coordinator.h
@@ -13,6 +13,7 @@
 
 @protocol AIMPrototypeAnimationContextProvider;
 enum class AIMPrototypeEntrypoint;
+@protocol AIMPrototypeURLLoader;
 class Browser;
 @protocol OmniboxPopupPresenterDelegate;
 
@@ -36,6 +37,7 @@
                                    browser:(Browser*)browser
                                 entrypoint:(AIMPrototypeEntrypoint)entrypoint
                                      query:(NSString*)query
+                                 URLLoader:(id<AIMPrototypeURLLoader>)URLLoader
     NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
diff --git a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_coordinator.mm b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_coordinator.mm
index 29f4f9a..3418b04e 100644
--- a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_coordinator.mm
+++ b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_coordinator.mm
@@ -12,7 +12,6 @@
 #import "ios/chrome/browser/aim/prototype/coordinator/aim_omnibox_client.h"
 #import "ios/chrome/browser/aim/prototype/coordinator/aim_prototype_entrypoint.h"
 #import "ios/chrome/browser/aim/prototype/coordinator/aim_prototype_mediator.h"
-#import "ios/chrome/browser/aim/prototype/coordinator/aim_prototype_navigation_mediator.h"
 #import "ios/chrome/browser/aim/prototype/ui/aim_prototype_view_controller.h"
 #import "ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.h"
 #import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
@@ -46,8 +45,7 @@
 const size_t kMaxURLDisplayChars = 32 * 1024;
 }
 
-@interface AIMPrototypeCoordinator () <AIMPrototypeNavigationMediatorDelegate,
-                                       AIMPrototypeViewControllerDelegate,
+@interface AIMPrototypeCoordinator () <AIMPrototypeViewControllerDelegate,
                                        LocationBarModelDelegateWebStateProvider,
                                        LocationBarURLLoader,
                                        PHPickerViewControllerDelegate,
@@ -61,7 +59,6 @@
 @implementation AIMPrototypeCoordinator {
   AIMPrototypeViewController* _viewController;
   AIMPrototypeMediator* _mediator;
-  AIMPrototypeNavigationMediator* _navigationMediator;
   id<VoiceSearchController> _voiceSearchController;
   /// The prewarmed picker as it takes time to appear.
   PHPickerViewController* _picker;
@@ -69,6 +66,8 @@
   AIMPrototypeEntrypoint _entrypoint;
   /// Optional query inserted into the omnibox at start.
   NSString* _query;
+  /// The URLLoader to pass to the mediator.
+  __weak id<AIMPrototypeURLLoader> _URLLoader;
   /// Coordinator of the omnibox.
   OmniboxCoordinator* _omniboxCoordinator;
   // API endpoint for omnibox.
@@ -80,11 +79,14 @@
 - (instancetype)initWithBaseViewController:(UIViewController*)baseViewController
                                    browser:(Browser*)browser
                                 entrypoint:(AIMPrototypeEntrypoint)entrypoint
-                                     query:(NSString*)query {
+                                     query:(NSString*)query
+                                 URLLoader:
+                                     (id<AIMPrototypeURLLoader>)URLLoader {
   self = [super initWithBaseViewController:baseViewController browser:browser];
   if (self) {
     _entrypoint = entrypoint;
     _query = query;
+    _URLLoader = URLLoader;
   }
   return self;
 }
@@ -96,8 +98,6 @@
   _voiceSearchController =
       ios::provider::CreateVoiceSearchController(self.browser);
 
-  UrlLoadingBrowserAgent* urlLoadingBrowserAgent =
-      UrlLoadingBrowserAgent::FromBrowser(self.browser);
   TemplateURLService* templateURLService =
       ios::TemplateURLServiceFactory::GetForProfile(self.profile);
   signin::IdentityManager* identityManager =
@@ -113,21 +113,13 @@
           /*enable_multi_context_input_flow=*/false,
           /*enable_viewport_images=*/true);
 
-  web::WebState::CreateParams params =
-      web::WebState::CreateParams(self.profile);
-  _navigationMediator = [[AIMPrototypeNavigationMediator alloc]
-      initWithUrlLoadingBrowserAgent:urlLoadingBrowserAgent
-                      webStateParams:params];
-  _navigationMediator.consumer = _viewController;
-  _navigationMediator.delegate = self;
-
   FaviconLoader* faviconLoader =
       IOSChromeFaviconLoaderFactory::GetForProfile(self.profile);
   _mediator = [[AIMPrototypeMediator alloc]
       initWithComposeboxQueryController:std::move(composeboxQueryController)
                            webStateList:self.browser->GetWebStateList()
                           faviconLoader:faviconLoader];
-  _mediator.urlLoader = _navigationMediator;
+  _mediator.URLLoader = _URLLoader;
   _mediator.consumer = _viewController;
   _viewController.mutator = _mediator;
   _voiceSearchController.dispatcher = _mediator;
@@ -166,6 +158,7 @@
 }
 
 - (void)stop {
+  _viewController.mutator = nil;
   _viewController = nil;
   _picker = nil;
   [_voiceSearchController dismissMicPermissionHelp];
@@ -173,9 +166,9 @@
   _voiceSearchController.dispatcher = nil;
   _voiceSearchController = nil;
   [_mediator disconnect];
+  _mediator.URLLoader = nil;
+  _mediator.consumer = nil;
   _mediator = nil;
-  [_navigationMediator disconnect];
-  _navigationMediator = nil;
   [_omniboxCoordinator endEditing];
   [_omniboxCoordinator stop];
   _omniboxCoordinator = nil;
@@ -291,14 +284,6 @@
   [picker dismissViewControllerAnimated:YES completion:nil];
 }
 
-#pragma mark - AIMPrototypeNavigationMediatorDelegate
-
-- (void)dismissAIMPrototype {
-  id<BrowserCoordinatorCommands> commands = HandlerForProtocol(
-      self.browser->GetCommandDispatcher(), BrowserCoordinatorCommands);
-  [commands hideAIMPrototype];
-}
-
 #pragma mark - LocationBarURLLoader
 
 - (void)loadGURLFromLocationBar:(const GURL&)url
@@ -353,4 +338,12 @@
   return _locationBarModel.get();
 }
 
+#pragma mark - Private
+
+- (void)dismissAIMPrototype {
+  id<BrowserCoordinatorCommands> commands = HandlerForProtocol(
+      self.browser->GetCommandDispatcher(), BrowserCoordinatorCommands);
+  [commands hideAIMPrototype];
+}
+
 @end
diff --git a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_mediator.h b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_mediator.h
index 4d9a441..4b05ea89 100644
--- a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_mediator.h
+++ b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_mediator.h
@@ -28,7 +28,7 @@
                                             LoadQueryCommands>
 
 @property(nonatomic, weak) id<AIMPrototypeConsumer> consumer;
-@property(nonatomic, weak) id<AIMPrototypeURLLoader> urlLoader;
+@property(nonatomic, weak) id<AIMPrototypeURLLoader> URLLoader;
 
 - (instancetype)initWithComposeboxQueryController:
                     (std::unique_ptr<ComposeboxQueryControllerIOS>)
diff --git a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_mediator.mm b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_mediator.mm
index c057f31..1beec1fe 100644
--- a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_mediator.mm
+++ b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_mediator.mm
@@ -249,7 +249,7 @@
   if (!_AIModeEnabled) {
     URL = net::AppendOrReplaceQueryParameter(URL, "udm", "24");
   }
-  [self.urlLoader loadURL:URL];
+  [self.URLLoader loadURL:URL];
 }
 
 - (void)setAIModeEnabled:(BOOL)enabled {
@@ -572,9 +572,12 @@
               destinationURL:(const GURL&)destinationURL
                 isSearchType:(BOOL)isSearchType {
   if (isSearchType) {
+    if (IsAimURL(destinationURL)) {
+      [self.consumer setAIModeEnabled:YES];
+    }
     [self sendText:[NSString cr_fromString16:text]];
   } else {
-    [self.urlLoader loadURL:destinationURL];
+    [self.URLLoader loadURL:destinationURL];
   }
 }
 
diff --git a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_navigation_mediator.h b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_navigation_mediator.h
index 50f43af6..fd86da0b 100644
--- a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_navigation_mediator.h
+++ b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_navigation_mediator.h
@@ -16,7 +16,12 @@
 
 // Delegate for the AIM prototype navigation mediator.
 @protocol AIMPrototypeNavigationMediatorDelegate
-- (void)dismissAIMPrototype;
+
+// Called when the navigation mediator requires the AIM prototype to be
+// dismissed.
+- (void)navigationMediatorDidFinish:
+    (AIMPrototypeNavigationMediator*)navigationMediator;
+
 @end
 
 // A mediator for the AIM prototype's navigation.
diff --git a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_navigation_mediator.mm b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_navigation_mediator.mm
index c517f14..f24155c5 100644
--- a/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_navigation_mediator.mm
+++ b/ios/chrome/browser/aim/prototype/coordinator/aim_prototype_navigation_mediator.mm
@@ -157,7 +157,7 @@
   // performed, for example. This hack postpones the dismiss action.
   __weak __typeof(self) weakSelf = self;
   base::OnceClosure completion = base::BindOnce(^{
-    [weakSelf.delegate dismissAIMPrototype];
+    [weakSelf.delegate navigationMediatorDidFinish:weakSelf];
   });
   constexpr base::TimeDelta kDelay = base::Seconds(0.5);
   base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
diff --git a/ios/chrome/browser/aim/prototype/ui/aim_prototype_container_view_controller.h b/ios/chrome/browser/aim/prototype/ui/aim_prototype_container_view_controller.h
index e9e19aa..d10deead 100644
--- a/ios/chrome/browser/aim/prototype/ui/aim_prototype_container_view_controller.h
+++ b/ios/chrome/browser/aim/prototype/ui/aim_prototype_container_view_controller.h
@@ -7,6 +7,7 @@
 
 #import <UIKit/UIKit.h>
 
+#import "ios/chrome/browser/aim/prototype/ui/aim_prototype_navigation_consumer.h"
 #import "ios/chrome/browser/omnibox/ui/popup/omnibox_popup_presenter.h"
 
 @class AIMPrototypeContainerViewController;
@@ -19,7 +20,8 @@
 
 // View Controller that contains the AIM prototype, presenting it modally.
 @interface AIMPrototypeContainerViewController
-    : UIViewController <OmniboxPopupPresenterDelegate>
+    : UIViewController <AIMPrototypeNavigationConsumer,
+                        OmniboxPopupPresenterDelegate>
 
 // The delegate.
 @property(nonatomic, weak) id<AIMPrototypeContainerViewControllerDelegate>
diff --git a/ios/chrome/browser/aim/prototype/ui/aim_prototype_container_view_controller.mm b/ios/chrome/browser/aim/prototype/ui/aim_prototype_container_view_controller.mm
index 3d79ab0..b598699 100644
--- a/ios/chrome/browser/aim/prototype/ui/aim_prototype_container_view_controller.mm
+++ b/ios/chrome/browser/aim/prototype/ui/aim_prototype_container_view_controller.mm
@@ -21,10 +21,14 @@
 }  // namespace
 
 @implementation AIMPrototypeContainerViewController {
+  // Close button.
+  UIButton* _closeButton;
   // Container for the input.
   UIView* _inputContainer;
-  /// Container for the omnibox popup.
+  // Container for the omnibox popup.
   UIView* _omniboxPopupContainer;
+  // WebView for the SRP, when AI Mode Immersive SRP is enabled.
+  UIView* _webView;
 }
 
 - (void)viewDidLoad {
@@ -34,9 +38,9 @@
 
   UILayoutGuide* safeAreaGuide = self.view.safeAreaLayoutGuide;
 
-  // Close button
-  UIButton* closeButton = [UIButton buttonWithType:UIButtonTypeSystem];
-  closeButton.translatesAutoresizingMaskIntoConstraints = NO;
+  // Close button.
+  _closeButton = [UIButton buttonWithType:UIButtonTypeSystem];
+  _closeButton.translatesAutoresizingMaskIntoConstraints = NO;
   UIImageSymbolConfiguration* symbolConfiguration = [UIImageSymbolConfiguration
       configurationWithPointSize:kCloseButtonSize
                           weight:UIImageSymbolWeightRegular
@@ -49,21 +53,22 @@
                               colorWithAlphaComponent:kCloseButtonAlpha],
                           [UIColor tertiarySystemFillColor]
                         ]);
-  [closeButton setImage:buttonImage forState:UIControlStateNormal];
+  [_closeButton setImage:buttonImage forState:UIControlStateNormal];
 
-  [closeButton addTarget:self
-                  action:@selector(closeButtonTapped)
-        forControlEvents:UIControlEventTouchUpInside];
-  [self.view addSubview:closeButton];
+  [_closeButton addTarget:self
+                   action:@selector(closeButtonTapped)
+         forControlEvents:UIControlEventTouchUpInside];
+  [self.view addSubview:_closeButton];
 
   [NSLayoutConstraint activateConstraints:@[
-    [closeButton.topAnchor constraintEqualToAnchor:safeAreaGuide.topAnchor
-                                          constant:kCloseButtonPadding],
-    [closeButton.trailingAnchor
+    [_closeButton.topAnchor constraintEqualToAnchor:safeAreaGuide.topAnchor
+                                           constant:kCloseButtonPadding],
+    [_closeButton.trailingAnchor
         constraintEqualToAnchor:safeAreaGuide.trailingAnchor
                        constant:-kCloseButtonPadding],
-    [closeButton.heightAnchor constraintEqualToConstant:kCloseButtonSize],
-    [closeButton.widthAnchor constraintEqualToAnchor:closeButton.heightAnchor],
+    [_closeButton.heightAnchor constraintEqualToConstant:kCloseButtonSize],
+    [_closeButton.widthAnchor
+        constraintEqualToAnchor:_closeButton.heightAnchor],
   ]];
 
   // Omnibox popup container.
@@ -74,7 +79,7 @@
 
   [NSLayoutConstraint activateConstraints:@[
     [_omniboxPopupContainer.topAnchor
-        constraintEqualToAnchor:closeButton.bottomAnchor],
+        constraintEqualToAnchor:_closeButton.bottomAnchor],
     [_omniboxPopupContainer.leadingAnchor
         constraintEqualToAnchor:self.view.leadingAnchor],
     [_omniboxPopupContainer.trailingAnchor
@@ -114,6 +119,26 @@
   [inputViewController didMoveToParentViewController:self];
 }
 
+#pragma mark - AIMPrototypeNavigationConsumer
+
+- (void)setWebView:(UIView*)webView {
+  if (_webView == webView) {
+    return;
+  }
+  [_webView removeFromSuperview];
+  _webView = webView;
+  if (webView) {
+    webView.translatesAutoresizingMaskIntoConstraints = NO;
+    [self.view insertSubview:webView atIndex:0];
+    AddSameConstraintsToSides(webView, self.view.safeAreaLayoutGuide,
+                              LayoutSides::kLeading | LayoutSides::kTrailing);
+    [NSLayoutConstraint activateConstraints:@[
+      [webView.topAnchor constraintEqualToAnchor:_closeButton.bottomAnchor],
+      [webView.bottomAnchor constraintEqualToAnchor:_inputContainer.topAnchor],
+    ]];
+  }
+}
+
 #pragma mark - Action
 
 - (void)closeButtonTapped {
diff --git a/ios/chrome/browser/aim/prototype/ui/aim_prototype_view_controller.h b/ios/chrome/browser/aim/prototype/ui/aim_prototype_view_controller.h
index 0052430..0737432 100644
--- a/ios/chrome/browser/aim/prototype/ui/aim_prototype_view_controller.h
+++ b/ios/chrome/browser/aim/prototype/ui/aim_prototype_view_controller.h
@@ -10,7 +10,6 @@
 #import "ios/chrome/browser/aim/prototype/ui/aim_prototype_animation_context_provider.h"
 #import "ios/chrome/browser/aim/prototype/ui/aim_prototype_consumer.h"
 #import "ios/chrome/browser/aim/prototype/ui/aim_prototype_mutator.h"
-#import "ios/chrome/browser/aim/prototype/ui/aim_prototype_navigation_consumer.h"
 
 @protocol AIMPrototypeMutator;
 @class AIMPrototypeViewController;
@@ -33,8 +32,7 @@
 // View controller for the AIM prototype.
 @interface AIMPrototypeViewController
     : UIViewController <AIMPrototypeAnimationContextProvider,
-                        AIMPrototypeConsumer,
-                        AIMPrototypeNavigationConsumer>
+                        AIMPrototypeConsumer>
 
 @property(nonatomic, weak) id<AIMPrototypeViewControllerDelegate> delegate;
 @property(nonatomic, weak) id<AIMPrototypeMutator> mutator;
diff --git a/ios/chrome/browser/aim/prototype/ui/aim_prototype_view_controller.mm b/ios/chrome/browser/aim/prototype/ui/aim_prototype_view_controller.mm
index 3243ac20..825434f5 100644
--- a/ios/chrome/browser/aim/prototype/ui/aim_prototype_view_controller.mm
+++ b/ios/chrome/browser/aim/prototype/ui/aim_prototype_view_controller.mm
@@ -127,8 +127,6 @@
   UIView* _omniboxContainer;
   /// A spacer view used in the stack view.
   UIView* _spacerView;
-  /// The WebView for the SRP.
-  UIView* _webView;
 
   /// The cancellable callback for updating the glow effect.
   base::CancelableOnceClosure _updateGlowCallback;
@@ -457,26 +455,6 @@
   _micButton.hidden = hidden;
 }
 
-#pragma mark - AIMPrototypeNavigationConsumer
-
-- (void)setWebView:(UIView*)webView {
-  if (_webView == webView) {
-    return;
-  }
-  [_webView removeFromSuperview];
-  _webView = webView;
-  if (webView) {
-    webView.translatesAutoresizingMaskIntoConstraints = NO;
-    [self.view insertSubview:webView atIndex:0];
-    AddSameConstraintsToSides(
-        webView, self.view,
-        LayoutSides::kTop | LayoutSides::kLeading | LayoutSides::kTrailing);
-    [webView.bottomAnchor
-        constraintEqualToAnchor:_inputPlateContainerView.topAnchor]
-        .active = YES;
-  }
-}
-
 #pragma mark - Actions
 
 - (void)galleryButtonTapped {
diff --git a/ios/chrome/browser/alert_view/ui_bundled/alert_view_controller.mm b/ios/chrome/browser/alert_view/ui_bundled/alert_view_controller.mm
index 0bdbbc4a..ccc8a02 100644
--- a/ios/chrome/browser/alert_view/ui_bundled/alert_view_controller.mm
+++ b/ios/chrome/browser/alert_view/ui_bundled/alert_view_controller.mm
@@ -911,7 +911,7 @@
   LottieAnimationConfiguration* config =
       [[LottieAnimationConfiguration alloc] init];
   config.animationName = animationAssetName;
-  config.loopAnimationCount = -1;  // Always loop.
+  config.shouldLoop = YES;
   return ios::provider::GenerateLottieAnimation(config);
 }
 
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_table_view_controller_model_delegate.h b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_table_view_controller_model_delegate.h
index ac50b31..2456d9cb 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_table_view_controller_model_delegate.h
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_table_view_controller_model_delegate.h
@@ -8,12 +8,14 @@
 #import <Foundation/Foundation.h>
 
 @class ConsistencyAccountChooserViewController;
+@class IdentityItemConfigurator;
 
 // Protocol to get the model.
 @protocol ConsistencyAccountChooserTableViewControllerModelDelegate <NSObject>
 
 // Returns all the configurators to generate model items.
-@property(nonatomic, strong, readonly) NSArray* sortedIdentityItemConfigurators;
+@property(nonatomic, strong, readonly)
+    NSArray<IdentityItemConfigurator*>* sortedIdentityItemConfigurators;
 
 @end
 
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_view_controller.mm b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_view_controller.mm
index bd7ed75..020546d 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_view_controller.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_view_controller.mm
@@ -48,7 +48,7 @@
     [subView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
     [subView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
   ]];
-  [self didMoveToParentViewController:self.tableViewController];
+  [self.tableViewController didMoveToParentViewController:self];
 }
 
 - (void)viewDidLayoutSubviews {
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_coordinator_unittest.mm b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_coordinator_unittest.mm
index f1f3ee4..2598539f 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_coordinator_unittest.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_coordinator_unittest.mm
@@ -7,6 +7,7 @@
 #import <UIKit/UIKit.h>
 
 #import "components/signin/core/browser/account_reconcilor.h"
+#import "components/test/ios/test_utils.h"
 #import "ios/chrome/app/profile/profile_state.h"
 #import "ios/chrome/browser/authentication/ui_bundled/authentication_test_util.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_coordinator.h"
@@ -112,19 +113,15 @@
     OCMExpect([(id)mediator_mock_ alloc]).andReturn(mediator_mock_);
     OCMExpect(
         [mediator_mock_
-            initWithAccountManagerService:reinterpret_cast<
-                                              ChromeAccountManagerService*>(
-                                              [OCMArg anyPointer])
-                    authenticationService:reinterpret_cast<
-                                              AuthenticationService*>(
-                                              [OCMArg anyPointer])
-                          identityManager:reinterpret_cast<
-                                              signin::IdentityManager*>(
-                                              [OCMArg anyPointer])
-                        accountReconcilor:reinterpret_cast<AccountReconcilor*>(
-                                              [OCMArg anyPointer])
-                          userPrefService:reinterpret_cast<PrefService*>(
-                                              [OCMArg anyPointer])
+            initWithAccountManagerService:ios::OCM::AnyPointer<
+                                              ChromeAccountManagerService>()
+                    authenticationService:ios::OCM::AnyPointer<
+                                              AuthenticationService>()
+                          identityManager:ios::OCM::AnyPointer<
+                                              signin::IdentityManager>()
+                        accountReconcilor:ios::OCM::AnyPointer<
+                                              AccountReconcilor>()
+                          userPrefService:ios::OCM::AnyPointer<PrefService>()
                               accessPoint:access_point_])
         .ignoringNonObjectArgs()
         .andReturn(mediator_mock_);
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_mediator_unittest.mm b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_mediator_unittest.mm
index b0252c4..274eabf 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_mediator_unittest.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_mediator_unittest.mm
@@ -172,16 +172,15 @@
     }
     OCMExpect(
         [mediator_delegate_mock_
-            trackWebSigninWithIdentityManager:static_cast<
-                                                  signin::IdentityManager*>(
-                                                  [OCMArg anyPointer])
-                            accountReconcilor:static_cast<AccountReconcilor*>(
-                                                  [OCMArg anyPointer])
+            trackWebSigninWithIdentityManager:ios::OCM::AnyPointer<
+                                                  signin::IdentityManager>()
+                            accountReconcilor:ios::OCM::AnyPointer<
+                                                  AccountReconcilor>()
                                 signinAccount:CoreAccountId()
-                                 withCallback:
-                                     static_cast<base::RepeatingCallback<void(
-                                         signin::WebSigninTracker::Result)>*>(
-                                         [OCMArg anyPointer])
+                                 withCallback:ios::OCM::AnyPointer<
+                                                  base::RepeatingCallback<void(
+                                                      signin::WebSigninTracker::
+                                                          Result)>>()
                                   withTimeout:std::nullopt])
         .ignoringNonObjectArgs()
         .andAssignStructParameterAtAddressToVariable(captured_callback_, 3);
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/logging/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/signin/logging/BUILD.gn
index e75afcfd..f93f038 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/logging/BUILD.gn
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/logging/BUILD.gn
@@ -8,9 +8,9 @@
   sources = [
     "first_run_signin_logger.h",
     "first_run_signin_logger.mm",
+    "fullscreen_signin_promo_logger.h",
+    "fullscreen_signin_promo_logger.mm",
     "signin_logger.h",
-    "upgrade_signin_logger.h",
-    "upgrade_signin_logger.mm",
     "user_signin_logger.h",
     "user_signin_logger.mm",
   ]
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/logging/upgrade_signin_logger.h b/ios/chrome/browser/authentication/ui_bundled/signin/logging/fullscreen_signin_promo_logger.h
similarity index 86%
rename from ios/chrome/browser/authentication/ui_bundled/signin/logging/upgrade_signin_logger.h
rename to ios/chrome/browser/authentication/ui_bundled/signin/logging/fullscreen_signin_promo_logger.h
index b4068c7..019a469 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/logging/upgrade_signin_logger.h
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/logging/fullscreen_signin_promo_logger.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_LOGGING_UPGRADE_SIGNIN_LOGGER_H_
-#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_LOGGING_UPGRADE_SIGNIN_LOGGER_H_
+#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_LOGGING_FULLSCREEN_SIGNIN_PROMO_LOGGER_H_
+#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_LOGGING_FULLSCREEN_SIGNIN_PROMO_LOGGER_H_
 
 #import "ios/chrome/browser/authentication/ui_bundled/signin/logging/user_signin_logger.h"
 
@@ -19,7 +19,7 @@
 }  // namespace signin_metrics
 
 // Logs metrics for Chrome upgrade operations.
-@interface UpgradeSigninLogger : NSObject <SigninLogger>
+@interface FullscreenSigninPromoLogger : NSObject <SigninLogger>
 
 - (instancetype)init NS_UNAVAILABLE;
 - (instancetype)initWithAccessPoint:(signin_metrics::AccessPoint)accessPoint
@@ -34,4 +34,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_LOGGING_UPGRADE_SIGNIN_LOGGER_H_
+#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_LOGGING_FULLSCREEN_SIGNIN_PROMO_LOGGER_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/logging/upgrade_signin_logger.mm b/ios/chrome/browser/authentication/ui_bundled/signin/logging/fullscreen_signin_promo_logger.mm
similarity index 96%
rename from ios/chrome/browser/authentication/ui_bundled/signin/logging/upgrade_signin_logger.mm
rename to ios/chrome/browser/authentication/ui_bundled/signin/logging/fullscreen_signin_promo_logger.mm
index edf2af57..4f04ce6 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/logging/upgrade_signin_logger.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/logging/fullscreen_signin_promo_logger.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/authentication/ui_bundled/signin/logging/upgrade_signin_logger.h"
+#import "ios/chrome/browser/authentication/ui_bundled/signin/logging/fullscreen_signin_promo_logger.h"
 
 #import "base/metrics/histogram_macros.h"
 #import "base/metrics/user_metrics.h"
@@ -19,7 +19,7 @@
 using signin_metrics::PromoAction;
 using signin_metrics::RecordSigninUserActionForAccessPoint;
 
-@implementation UpgradeSigninLogger {
+@implementation FullscreenSigninPromoLogger {
   // Identity manager to retrieve Chrome identities.
   raw_ptr<signin::IdentityManager> _identityManager;
 
@@ -72,7 +72,7 @@
 
   // Records in user defaults that the promo has been shown as well as the
   // number of times it's been displayed.
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       _identityManager, _accountManagerService, version_info::GetVersion());
   NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults];
   int promoSeenCount =
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/promo/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/signin/promo/BUILD.gn
index febe9c8..d5b8655 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/promo/BUILD.gn
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/promo/BUILD.gn
@@ -4,10 +4,10 @@
 
 source_set("promo") {
   sources = [
-    "signin_fullscreen_promo_display_handler.h",
-    "signin_fullscreen_promo_display_handler.mm",
-    "signin_fullscreen_promo_scene_agent.h",
-    "signin_fullscreen_promo_scene_agent.mm",
+    "fullscreen_signin_promo_display_handler.h",
+    "fullscreen_signin_promo_display_handler.mm",
+    "fullscreen_signin_promo_scene_agent.h",
+    "fullscreen_signin_promo_scene_agent.mm",
   ]
   deps = [
     "//components/feature_engagement/public",
@@ -30,7 +30,7 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "signin_fullscreen_promo_scene_agent_unittest.mm" ]
+  sources = [ "fullscreen_signin_promo_scene_agent_unittest.mm" ]
 
   deps = [
     ":promo",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_display_handler.h b/ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_display_handler.h
similarity index 76%
rename from ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_display_handler.h
rename to ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_display_handler.h
index 13816359..57133ded3 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_display_handler.h
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_display_handler.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_PROMO_SIGNIN_FULLSCREEN_PROMO_DISPLAY_HANDLER_H_
-#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_PROMO_SIGNIN_FULLSCREEN_PROMO_DISPLAY_HANDLER_H_
+#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_PROMO_FULLSCREEN_SIGNIN_PROMO_DISPLAY_HANDLER_H_
+#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_PROMO_FULLSCREEN_SIGNIN_PROMO_DISPLAY_HANDLER_H_
 
 #import "ios/chrome/browser/promos_manager/ui_bundled/standard_promo_display_handler.h"
 
-@interface SigninFullscreenPromoDisplayHandler
+@interface FullscreenSigninPromoDisplayHandler
     : NSObject <StandardPromoDisplayHandler>
 
 #pragma mark - PromoProtocol
@@ -17,4 +17,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_PROMO_SIGNIN_FULLSCREEN_PROMO_DISPLAY_HANDLER_H_
+#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_PROMO_FULLSCREEN_SIGNIN_PROMO_DISPLAY_HANDLER_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_display_handler.mm b/ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_display_handler.mm
similarity index 75%
rename from ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_display_handler.mm
rename to ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_display_handler.mm
index 33cc98d..4db40ed 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_display_handler.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_display_handler.mm
@@ -2,24 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_display_handler.h"
+#import "ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_display_handler.h"
 
 #import "components/feature_engagement/public/feature_constants.h"
 #import "ios/chrome/browser/promos_manager/model/constants.h"
 #import "ios/chrome/browser/promos_manager/model/promo_config.h"
 
-@implementation SigninFullscreenPromoDisplayHandler
+@implementation FullscreenSigninPromoDisplayHandler
 
 #pragma mark - StandardPromoDisplayHandler
 
 - (void)handleDisplay {
-  [self.handler showSigninPromo];
+  [self.handler showFullscreenSigninPromo];
 }
 
 #pragma mark - PromoProtocol
 
 - (PromoConfig)config {
-  return PromoConfig(promos_manager::Promo::SigninFullscreen,
+  return PromoConfig(promos_manager::Promo::FullscreenSignin,
                      &feature_engagement::kIPHiOSPromoSigninFullscreenFeature);
 }
 
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_scene_agent.h b/ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_scene_agent.h
similarity index 79%
rename from ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_scene_agent.h
rename to ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_scene_agent.h
index 649f1f57..785157af 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_scene_agent.h
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_scene_agent.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_PROMO_SIGNIN_FULLSCREEN_PROMO_SCENE_AGENT_H_
-#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_PROMO_SIGNIN_FULLSCREEN_PROMO_SCENE_AGENT_H_
+#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_PROMO_FULLSCREEN_SIGNIN_PROMO_SCENE_AGENT_H_
+#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_PROMO_FULLSCREEN_SIGNIN_PROMO_SCENE_AGENT_H_
 
 #import <Foundation/Foundation.h>
 
@@ -21,9 +21,9 @@
 class AuthenticationService;
 class PrefService;
 
-// A scene agent that registers the Signin fullscreen promo in the promo
+// A scene agent that registers the Fullscreen Signin promo in the promo
 // manager.
-@interface SigninFullscreenPromoSceneAgent : ObservingSceneAgent
+@interface FullscreenSigninPromoSceneAgent : ObservingSceneAgent
 
 - (instancetype)initWithPromosManager:(PromosManager*)promosManager
                           authService:(AuthenticationService*)authService
@@ -32,4 +32,4 @@
                           prefService:(PrefService*)prefService;
 @end
 
-#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_PROMO_SIGNIN_FULLSCREEN_PROMO_SCENE_AGENT_H_
+#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_PROMO_FULLSCREEN_SIGNIN_PROMO_SCENE_AGENT_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_scene_agent.mm b/ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_scene_agent.mm
similarity index 93%
rename from ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_scene_agent.mm
rename to ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_scene_agent.mm
index 213f021..88c39ac 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_scene_agent.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_scene_agent.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_scene_agent.h"
+#import "ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_scene_agent.h"
 
 #import "base/memory/raw_ptr.h"
 #import "base/notreached.h"
@@ -25,12 +25,12 @@
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/signin/model/authentication_service.h"
 
-@interface SigninFullscreenPromoSceneAgent () <
+@interface FullscreenSigninPromoSceneAgent () <
     ProfileStateObserver,
     IdentityManagerObserverBridgeDelegate>
 @end
 
-@implementation SigninFullscreenPromoSceneAgent {
+@implementation FullscreenSigninPromoSceneAgent {
   // The PromosManager used by the scene agent to manage promo display.
   raw_ptr<PromosManager> _promosManager;
 
@@ -123,11 +123,11 @@
               ->GetProfile(),
           version_info::GetVersion())) {
     _promosManager->RegisterPromoForContinuousDisplay(
-        promos_manager::Promo::SigninFullscreen);
+        promos_manager::Promo::FullscreenSignin);
     return;
   }
 
-  _promosManager->DeregisterPromo(promos_manager::Promo::SigninFullscreen);
+  _promosManager->DeregisterPromo(promos_manager::Promo::FullscreenSignin);
 }
 
 #pragma mark - IdentityManagerObserverBridgeDelegate
@@ -141,7 +141,7 @@
     if (skipReason != history_sync::HistorySyncSkipReason::kNone) {
       // Deregister the promo if the user signed in and the history sync can be
       // skipped.
-      _promosManager->DeregisterPromo(promos_manager::Promo::SigninFullscreen);
+      _promosManager->DeregisterPromo(promos_manager::Promo::FullscreenSignin);
     }
   }
 }
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_scene_agent_unittest.mm b/ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_scene_agent_unittest.mm
similarity index 86%
rename from ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_scene_agent_unittest.mm
rename to ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_scene_agent_unittest.mm
index 6037b407..b78b550 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_scene_agent_unittest.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_scene_agent_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_scene_agent.h"
+#import "ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_scene_agent.h"
 
 #import "base/memory/raw_ptr.h"
 #import "components/signin/public/base/signin_metrics.h"
@@ -34,9 +34,9 @@
 #import "third_party/ocmock/OCMock/OCMock.h"
 #import "third_party/ocmock/gtest_support.h"
 
-class SigninFullscreenPromoSceneAgentTest : public PlatformTest {
+class FullscreenSigninPromoSceneAgentTest : public PlatformTest {
  public:
-  SigninFullscreenPromoSceneAgentTest() : PlatformTest() {
+  FullscreenSigninPromoSceneAgentTest() : PlatformTest() {
     TestProfileIOS::Builder builder;
     builder.AddTestingFactory(
         AuthenticationServiceFactory::GetInstance(),
@@ -59,7 +59,7 @@
         AuthenticationServiceFactory::GetForProfile(profile_.get());
     identity_manager_ = IdentityManagerFactory::GetForProfile(profile_.get());
     promos_manager_ = std::make_unique<MockPromosManager>();
-    agent_ = [[SigninFullscreenPromoSceneAgent alloc]
+    agent_ = [[FullscreenSigninPromoSceneAgent alloc]
         initWithPromosManager:promos_manager_.get()
                   authService:authentication_service_
               identityManager:identity_manager_
@@ -78,7 +78,7 @@
     scene_state_.UIEnabled = YES;
   }
 
-  ~SigninFullscreenPromoSceneAgentTest() override {
+  ~FullscreenSigninPromoSceneAgentTest() override {
     profile_state_.profile = nullptr;
   }
 
@@ -100,7 +100,7 @@
 
  protected:
   IOSChromeScopedTestingLocalState scoped_testing_local_state_;
-  SigninFullscreenPromoSceneAgent* agent_;
+  FullscreenSigninPromoSceneAgent* agent_;
   web::WebTaskEnvironment task_environment_;
   syncer::TestSyncService sync_service_;
   StubBrowserProviderInterface* stub_browser_interface_provider_;
@@ -117,37 +117,37 @@
 
 // Tests that the sign-in fullscreen promo registers with the promo manager when
 // the eligibility criteria are met.
-TEST_F(SigninFullscreenPromoSceneAgentTest,
-       TestSigninFullscreenPromoRegistration) {
+TEST_F(FullscreenSigninPromoSceneAgentTest,
+       TestFullscreenSigninPromoRegistration) {
   const base::Version version_1_0("1.0");
   FakeSystemIdentity* fake_identity1 = [FakeSystemIdentity fakeIdentity1];
   FakeSystemIdentityManager::FromSystemIdentityManager(
       GetApplicationContext()->GetSystemIdentityManager())
       ->AddIdentity(fake_identity1);
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
 
   EXPECT_CALL(*promos_manager_.get(),
               RegisterPromoForContinuousDisplay(
-                  promos_manager::Promo::SigninFullscreen))
+                  promos_manager::Promo::FullscreenSignin))
       .Times(1);
   scene_state_.activationLevel = SceneActivationLevelForegroundActive;
 }
 
 // Tests that the sign-in fullscreen promo is not registered in the promo
 // manager when the eligibility criteria are not met.
-TEST_F(SigninFullscreenPromoSceneAgentTest,
-       TestSigninFullscreenPromoNoRegistration) {
+TEST_F(FullscreenSigninPromoSceneAgentTest,
+       TestFullscreenSigninPromoNoRegistration) {
   EXPECT_CALL(*promos_manager_.get(),
               RegisterPromoForContinuousDisplay(
-                  promos_manager::Promo::SigninFullscreen))
+                  promos_manager::Promo::FullscreenSignin))
       .Times(0);
   scene_state_.activationLevel = SceneActivationLevelForegroundActive;
 }
 
 // Tests that when a promo was previously registered, it is deregistered when
 // user is signed in and history sync is opted in.
-TEST_F(SigninFullscreenPromoSceneAgentTest,
+TEST_F(FullscreenSigninPromoSceneAgentTest,
        TestPromoDeregistrationWhenSignedInWithHistorySync) {
   // Register the promo.
   const base::Version version_1_0("1.0");
@@ -155,24 +155,24 @@
   FakeSystemIdentityManager::FromSystemIdentityManager(
       GetApplicationContext()->GetSystemIdentityManager())
       ->AddIdentity(fake_identity1);
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
   EXPECT_CALL(*promos_manager_.get(),
               RegisterPromoForContinuousDisplay(
-                  promos_manager::Promo::SigninFullscreen))
+                  promos_manager::Promo::FullscreenSignin))
       .Times(1);
   EXPECT_CALL(*promos_manager_.get(),
-              DeregisterPromo(promos_manager::Promo::SigninFullscreen))
+              DeregisterPromo(promos_manager::Promo::FullscreenSignin))
       .Times(0);
   scene_state_.activationLevel = SceneActivationLevelForegroundActive;
 
   // Sign in and enable history sync.
   EXPECT_CALL(*promos_manager_.get(),
               RegisterPromoForContinuousDisplay(
-                  promos_manager::Promo::SigninFullscreen))
+                  promos_manager::Promo::FullscreenSignin))
       .Times(0);
   EXPECT_CALL(*promos_manager_.get(),
-              DeregisterPromo(promos_manager::Promo::SigninFullscreen))
+              DeregisterPromo(promos_manager::Promo::FullscreenSignin))
       .Times(1);
   sync_service_.GetUserSettings()->SetSelectedType(
       syncer::UserSelectableType::kHistory, YES);
@@ -186,7 +186,7 @@
 
 // Tests that when a promo was previously registered, it is still registered
 // when user is signed in without history sync.
-TEST_F(SigninFullscreenPromoSceneAgentTest,
+TEST_F(FullscreenSigninPromoSceneAgentTest,
        TestPromoRegistrationWhenSignedInWithoutHistorySync) {
   // Register the promo.
   const base::Version version_1_0("1.0");
@@ -194,24 +194,24 @@
   FakeSystemIdentityManager::FromSystemIdentityManager(
       GetApplicationContext()->GetSystemIdentityManager())
       ->AddIdentity(fake_identity1);
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
   EXPECT_CALL(*promos_manager_.get(),
               RegisterPromoForContinuousDisplay(
-                  promos_manager::Promo::SigninFullscreen))
+                  promos_manager::Promo::FullscreenSignin))
       .Times(1);
   EXPECT_CALL(*promos_manager_.get(),
-              DeregisterPromo(promos_manager::Promo::SigninFullscreen))
+              DeregisterPromo(promos_manager::Promo::FullscreenSignin))
       .Times(0);
   scene_state_.activationLevel = SceneActivationLevelForegroundActive;
 
   // Sign in without history sync.
   EXPECT_CALL(*promos_manager_.get(),
               RegisterPromoForContinuousDisplay(
-                  promos_manager::Promo::SigninFullscreen))
+                  promos_manager::Promo::FullscreenSignin))
       .Times(0);
   EXPECT_CALL(*promos_manager_.get(),
-              DeregisterPromo(promos_manager::Promo::SigninFullscreen))
+              DeregisterPromo(promos_manager::Promo::FullscreenSignin))
       .Times(1);
   authentication_service_->SignIn(fake_identity1,
                                   signin_metrics::AccessPoint::kUnknown);
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/reauth/signin_reauth_coordinator_unittest.mm b/ios/chrome/browser/authentication/ui_bundled/signin/reauth/signin_reauth_coordinator_unittest.mm
index efd53d7f..4b3aa6a9 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/reauth/signin_reauth_coordinator_unittest.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/reauth/signin_reauth_coordinator_unittest.mm
@@ -105,9 +105,9 @@
                                               completion_block)]);
   [reauth_coordinator start];
 
-  OCMExpect([mock_delegate_ reauthFinishedWithResult:ReauthResult::kSuccess
-                                              gaiaID:static_cast<GaiaId*>(
-                                                         [OCMArg anyPointer])])
+  OCMExpect([mock_delegate_
+                reauthFinishedWithResult:ReauthResult::kSuccess
+                                  gaiaID:ios::OCM::AnyPointer<GaiaId>()])
       .andDo(^(NSInvocation* invocation) {
         GaiaId* gaia_id;
         [invocation getArgument:&gaia_id atIndex:3];
@@ -235,10 +235,9 @@
                                               completion_block)]);
   [reauth_coordinator start];
 
-  OCMExpect(
-      [[((id)mock_delegate_) ignoringNonObjectArgs]
-          reauthFinishedWithResult:ReauthResult::kSuccess
-                            gaiaID:static_cast<GaiaId*>([OCMArg anyPointer])])
+  OCMExpect([[((id)mock_delegate_) ignoringNonObjectArgs]
+                reauthFinishedWithResult:ReauthResult::kSuccess
+                                  gaiaID:ios::OCM::AnyPointer<GaiaId>()])
       .andDo(^(NSInvocation* invocation) {
         GaiaId* gaia_id;
         [invocation getArgument:&gaia_id atIndex:3];
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h
index b5efdfcc..eeffc1d 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h
@@ -96,18 +96,18 @@
                                   (const ChangeProfileContinuationProvider&)
                                       continuationProvider;
 
-// Returns a coordinator for upgrade sign-in workflow.
+// Returns a coordinator for fullscreen sign-in promo workflow.
 // `viewController` presents the sign-in.
 // `contextStyle` is used to customize content on screens.
 + (SigninCoordinator*)
-    upgradeSigninPromoCoordinatorWithBaseViewController:
+    fullscreenSigninPromoCoordinatorWithBaseViewController:
         (UIViewController*)viewController
-                                                browser:(Browser*)browser
-                                           contextStyle:
-                                               (SigninContextStyle)contextStyle
-                      changeProfileContinuationProvider:
-                          (const ChangeProfileContinuationProvider&)
-                              changeProfileContinuationProvider;
+                                                   browser:(Browser*)browser
+                                              contextStyle:(SigninContextStyle)
+                                                               contextStyle
+                         changeProfileContinuationProvider:
+                             (const ChangeProfileContinuationProvider&)
+                                 changeProfileContinuationProvider;
 
 // Returns a coordinator to add an account.
 // `viewController` presents the sign-in.
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.mm
index c4cd9105..a5215fa 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.mm
@@ -183,14 +183,14 @@
 }
 
 + (SigninCoordinator*)
-    upgradeSigninPromoCoordinatorWithBaseViewController:
+    fullscreenSigninPromoCoordinatorWithBaseViewController:
         (UIViewController*)viewController
-                                                browser:(Browser*)browser
-                                           contextStyle:
-                                               (SigninContextStyle)contextStyle
-                      changeProfileContinuationProvider:
-                          (const ChangeProfileContinuationProvider&)
-                              changeProfileContinuationProvider {
+                                                   browser:(Browser*)browser
+                                              contextStyle:(SigninContextStyle)
+                                                               contextStyle
+                         changeProfileContinuationProvider:
+                             (const ChangeProfileContinuationProvider&)
+                                 changeProfileContinuationProvider {
   CHECK(changeProfileContinuationProvider);
   AccessPoint accessPoint = AccessPoint::kFullscreenSigninPromo;
   PromoAction promoAction = PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO;
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.h b/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.h
index a233e038..42d6d442 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.h
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.h
@@ -101,9 +101,6 @@
   bool run_has_been_called_ = false;
 };
 
-// Returns the maximum allowed waiting time for the Account Capabilities API.
-base::TimeDelta GetWaitThresholdForCapabilities();
-
 // Returns true if this user sign-in upgrade should be shown for `profile`.
 bool ShouldPresentUserSigninUpgrade(ProfileIOS* profile,
                                     const base::Version& current_version);
@@ -112,13 +109,13 @@
 // actions is recorded to track why the sign-in dialog was not presented.
 bool ShouldPresentWebSignin(ProfileIOS* profile);
 
-// This method should be called when sign-in starts from the upgrade promo.
-// It records in user defaults:
+// This method should be called when sign-in starts from the fullscreen signin
+// promo. It records in user defaults:
 //   + the Chromium current version.
 //   + increases the sign-in promo display count.
 //   + Gaia ids list.
 // Separated out into a discrete function to allow overriding when testing.
-void RecordUpgradePromoSigninStarted(
+void RecordFullscreenSigninPromoStarted(
     signin::IdentityManager* identity_manager,
     ChromeAccountManagerService* account_manager_service,
     const base::Version& current_version);
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.mm b/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.mm
index 17329d5e..4ef3b21 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.mm
@@ -64,13 +64,6 @@
 
 namespace {
 
-// Maximum delay to wait for fetching the account capabilities before showing
-// the sign-in upgrade promo. If fetching the account capabilities takes more
-// than the delay, then the promo is suppressed - it may be shown on the next
-// start-up.
-constexpr base::TimeDelta kShowSigninUpgradePromoMaxDelay =
-    base::Milliseconds(200);
-
 // The duration between two signin upgrade promo trigger is randomly chosen
 // between [53..68) days.
 base::TimeDelta DurationBetweenPromoTriggers() {
@@ -171,21 +164,6 @@
 
 namespace signin {
 
-base::TimeDelta GetWaitThresholdForCapabilities() {
-  const base::CommandLine* command_line =
-      base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(
-          signin::kWaitThresholdMillisecondsForCapabilitiesApi)) {
-    std::string delayString = command_line->GetSwitchValueASCII(
-        signin::kWaitThresholdMillisecondsForCapabilitiesApi);
-    int commandLineDelay = 0;
-    if (base::StringToInt(delayString, &commandLineDelay)) {
-      return base::Milliseconds(commandLineDelay);
-    }
-  }
-  return kShowSigninUpgradePromoMaxDelay;
-}
-
 bool ShouldPresentUserSigninUpgrade(ProfileIOS* profile,
                                     const base::Version& current_version) {
   DCHECK(profile);
@@ -256,7 +234,7 @@
   std::optional<promos_manager::Promo> forced_promo =
       promos_manager::PromoForName(base::SysNSStringToUTF8(forced_promo_name));
   if (forced_promo.has_value() &&
-      forced_promo.value() == promos_manager::Promo::SigninFullscreen) {
+      forced_promo.value() == promos_manager::Promo::FullscreenSignin) {
     return true;
   }
 
@@ -362,7 +340,7 @@
   return true;
 }
 
-void RecordUpgradePromoSigninStarted(
+void RecordFullscreenSigninPromoStarted(
     signin::IdentityManager* identity_manager,
     ChromeAccountManagerService* account_manager_service,
     const base::Version& current_version) {
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils_unittest.mm b/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils_unittest.mm
index 41bd651..2aac2f4 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils_unittest.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils_unittest.mm
@@ -158,7 +158,7 @@
   fake_system_identity_manager()->AddIdentity(fake_identity2);
   const base::Version version_1_0("1.0");
 
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
 
   EXPECT_FALSE(
@@ -177,7 +177,7 @@
   fake_system_identity_manager()->AddIdentity(fake_identity2);
   const base::Version version_1_0("1.0");
 
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
   base::Time next_show_time =
       GetLocalState()->GetTime(prefs::kNextSSORecallTime);
@@ -196,7 +196,7 @@
   FakeSystemIdentity* fake_identity2 = [FakeSystemIdentity fakeIdentity2];
   fake_system_identity_manager()->AddIdentity(fake_identity2);
   const base::Version version_1_0("1.0");
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
   EXPECT_FALSE(
       signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_1_0));
@@ -211,7 +211,7 @@
   fake_system_identity_manager()->AddIdentity(fake_identity2);
   const base::Version version_1_0("1.0");
   const base::Version version_1_1("1.1");
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
   EXPECT_FALSE(
       signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_1_1));
@@ -226,7 +226,7 @@
   fake_system_identity_manager()->AddIdentity(fake_identity2);
   const base::Version version_1_0("1.0");
   const base::Version version_1_2("1.2");
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
   EXPECT_FALSE(
       signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_1_2));
@@ -241,7 +241,7 @@
   fake_system_identity_manager()->AddIdentity(fake_identity2);
   const base::Version version_1_0("1.0");
   const base::Version version_2_0("2.0");
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
   EXPECT_FALSE(
       signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_2_0));
@@ -256,7 +256,7 @@
   fake_system_identity_manager()->AddIdentity(fake_identity2);
   const base::Version version_1_0("1.0");
   const base::Version version_3_0("3.0");
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
   EXPECT_TRUE(
       signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_3_0));
@@ -280,9 +280,9 @@
   const base::Version version_1_0("1.0");
   const base::Version version_3_0("3.0");
   const base::Version version_5_0("5.0");
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_3_0);
   EXPECT_FALSE(
       signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_5_0));
@@ -317,9 +317,9 @@
                   feature_engagement::kIPHiOSPromoSigninFullscreenFeature)))
       .WillRepeatedly(testing::Return(event_list));
 
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_3_0);
   EXPECT_FALSE(
       signin::ShouldPresentUserSigninUpgrade(profile_.get(), version_5_0));
@@ -335,9 +335,9 @@
   const base::Version version_1_0("1.0");
   const base::Version version_3_0("3.0");
   const base::Version version_5_0("5.0");
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_3_0);
   FakeSystemIdentity* fake_identity = [FakeSystemIdentity fakeIdentity1];
   fake_system_identity_manager()->AddIdentity(fake_identity);
@@ -363,9 +363,9 @@
   const base::Version version_5_0("5.0");
   FakeSystemIdentity* fake_identity = [FakeSystemIdentity fakeIdentity1];
   fake_system_identity_manager()->AddIdentity(fake_identity);
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_3_0);
   fake_system_identity_manager()->ForgetIdentity(fake_identity,
                                                  base::DoNothing());
@@ -386,9 +386,9 @@
   const base::Version version_5_0("5.0");
   FakeSystemIdentity* fake_identity = [FakeSystemIdentity fakeIdentity1];
   fake_system_identity_manager()->AddIdentity(fake_identity);
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_3_0);
   fake_system_identity_manager()->ForgetIdentity(fake_identity,
                                                  base::DoNothing());
@@ -422,9 +422,9 @@
   const base::Version version_1_0("1.0");
   const base::Version version_3_0("3.0");
   const base::Version version_4_0("4.0");
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_3_0);
   FakeSystemIdentity* fake_identity = [FakeSystemIdentity fakeIdentity1];
   fake_system_identity_manager()->AddIdentity(fake_identity);
@@ -441,7 +441,7 @@
 TEST_F(SigninUtilsTest, TestWillNotShowNewAccountUntilTwoVersionBis) {
   const base::Version version_1_0("1.0");
   const base::Version version_2_0("2.0");
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
   FakeSystemIdentity* fake_identity = [FakeSystemIdentity fakeIdentity1];
   fake_system_identity_manager()->AddIdentity(fake_identity);
@@ -455,7 +455,7 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(switches::kFullscreenSignInPromoUseDate);
   const base::Version version_1_0("1.0");
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
   task_environment_.FastForwardBy(base::Days(100));
   FakeSystemIdentity* fake_identity = [FakeSystemIdentity fakeIdentity1];
@@ -476,7 +476,7 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(switches::kFullscreenSignInPromoUseDate);
   const base::Version version_1_0("1.0");
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
   task_environment_.FastForwardBy(base::Days(100));
   FakeSystemIdentity* fake_identity = [FakeSystemIdentity fakeIdentity1];
@@ -500,7 +500,7 @@
                                  signin_metrics::AccessPoint::kUnknown);
 
   const base::Version version_1_0("1.0");
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
   // Using task_environment_.FastForwardBy() causes this test to crash due to
   // sync internal logic.
@@ -522,7 +522,7 @@
   authentication_service->SignIn(identity,
                                  signin_metrics::AccessPoint::kUnknown);
   const base::Version version_1_0("1.0");
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
   // Using task_environment_.FastForwardBy() causes this test to crash due to
   // sync internal logic.
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/BUILD.gn
index e9668f2..7f8b042 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/BUILD.gn
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/BUILD.gn
@@ -37,6 +37,7 @@
     "//base/test:test_support",
     "//components/sync:test_support",
     "//components/sync/base:features",
+    "//components/test/ios",
     "//ios/chrome/app/profile",
     "//ios/chrome/browser/authentication/ui_bundled:continuation_test",
     "//ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator",
@@ -66,7 +67,7 @@
 source_set("eg2_tests") {
   configs += [ "//build/config/ios:xctest_config" ]
   testonly = true
-  sources = [ "upgrade_signin_promo_egtest.mm" ]
+  sources = [ "fullscreen_signin_promo_egtest.mm" ]
   deps = [
     "//base",
     "//base/test:test_support",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/upgrade_signin_promo_egtest.mm b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/fullscreen_signin_promo_egtest.mm
similarity index 98%
rename from ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/upgrade_signin_promo_egtest.mm
rename to ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/fullscreen_signin_promo_egtest.mm
index 42ff7ca..b5948f2 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/upgrade_signin_promo_egtest.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/fullscreen_signin_promo_egtest.mm
@@ -145,7 +145,7 @@
                                           PromoScreenPrimaryButtonMatcher()]
       performAction:grey_tap()];
   [ChromeEarlGreyUI waitForAppToIdle];
-  [self expectUpgradePromoMetricsAndPreferences];
+  [self expectFullscreenSigninPromoMetricsAndPreferences];
 }
 
 - (void)testHistoryOptInPromoNotShownWhenAlreadyGranted {
@@ -186,7 +186,7 @@
                                           PromoScreenPrimaryButtonMatcher()]
       performAction:grey_tap()];
   [ChromeEarlGreyUI waitForAppToIdle];
-  [self expectUpgradePromoMetricsAndPreferences];
+  [self expectFullscreenSigninPromoMetricsAndPreferences];
 }
 
 // Tests sign-in promo behavior in landscape. It should appears if and only if
@@ -211,7 +211,7 @@
 
 #pragma mark - Helpers
 
-- (void)expectUpgradePromoMetricsAndPreferences {
+- (void)expectFullscreenSigninPromoMetricsAndPreferences {
   NSError* error = [MetricsAppInterface
       expectUniqueSampleWithCount:1
                         forBucket:1
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator.mm
index e0dbb4b0d..df9f829 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator.mm
@@ -13,7 +13,7 @@
 #import "ios/chrome/browser/authentication/ui_bundled/continuation.h"
 #import "ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/fullscreen_signin_screen_coordinator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/history_sync/history_sync_coordinator.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/logging/upgrade_signin_logger.h"
+#import "ios/chrome/browser/authentication/ui_bundled/signin/logging/fullscreen_signin_promo_logger.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator+protected.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/uno_signin_screen_provider.h"
@@ -50,7 +50,7 @@
   ScreenProvider* _screenProvider;
 
   // The signin logger for the upgrade screen.
-  UpgradeSigninLogger* _upgradeSigninLogger;
+  FullscreenSigninPromoLogger* _fullscreenSigninPromoLogger;
 
   ChangeProfileContinuationProvider _continuationProvider;
 
@@ -81,17 +81,17 @@
         IdentityManagerFactory::GetForProfile(self.profile);
     ChromeAccountManagerService* accountManagerService =
         ChromeAccountManagerServiceFactory::GetForProfile(self.profile);
-    _upgradeSigninLogger =
-        [[UpgradeSigninLogger alloc] initWithAccessPoint:accessPoint
-                                             promoAction:promoAction
-                                         identityManager:identityManager
-                                   accountManagerService:accountManagerService];
+    _fullscreenSigninPromoLogger = [[FullscreenSigninPromoLogger alloc]
+          initWithAccessPoint:accessPoint
+                  promoAction:promoAction
+              identityManager:identityManager
+        accountManagerService:accountManagerService];
   }
   return self;
 }
 
 - (void)dealloc {
-  CHECK(!_upgradeSigninLogger, base::NotFatalUntil::M146);
+  CHECK(!_fullscreenSigninPromoLogger, base::NotFatalUntil::M146);
 }
 
 #pragma mark - BuggyAuthenticationViewOwner
@@ -106,7 +106,7 @@
   [super start];
   if (self.accessPoint == signin_metrics::AccessPoint::kFullscreenSigninPromo) {
     // TODO(crbug.com/41352590): Need to add `CHECK(accountManagerService)`.
-    [_upgradeSigninLogger logSigninStarted];
+    [_fullscreenSigninPromoLogger logSigninStarted];
   }
   _screenProvider = [[UnoSigninScreenProvider alloc] init];
   _navigationController =
@@ -142,8 +142,8 @@
       dismissViewControllerAnimated:animated
                          completion:nil];
   [self finishWithResult:SigninCoordinatorResultInterrupted identity:nil];
-  [_upgradeSigninLogger disconnect];
-  _upgradeSigninLogger = nil;
+  [_fullscreenSigninPromoLogger disconnect];
+  _fullscreenSigninPromoLogger = nil;
   DCHECK(!_navigationController);
   DCHECK(!_childCoordinator);
   DCHECK(!_screenProvider);
@@ -230,7 +230,8 @@
   if (self.accessPoint == signin_metrics::AccessPoint::kFullscreenSigninPromo) {
     // TODO(crbug.com/40074532): `addedAccount` is not always `NO`. Need to fix
     // that call to have the right value.
-    [_upgradeSigninLogger logSigninCompletedWithResult:result addedAccount:NO];
+    [_fullscreenSigninPromoLogger logSigninCompletedWithResult:result
+                                                  addedAccount:NO];
   }
   // When this coordinator is interrupted, `_childCoordinator` needs to be
   // stopped here.
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator_unittest.mm b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator_unittest.mm
index 55ca69e..707f4b6e 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator_unittest.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator_unittest.mm
@@ -12,6 +12,7 @@
 #import "base/test/metrics/histogram_tester.h"
 #import "base/test/metrics/user_action_tester.h"
 #import "components/sync/test/test_sync_service.h"
+#import "components/test/ios/test_utils.h"
 #import "ios/chrome/app/profile/profile_state.h"
 #import "ios/chrome/browser/authentication/ui_bundled/authentication_test_util.h"
 #import "ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/fullscreen_signin_screen_coordinator.h"
@@ -119,9 +120,8 @@
         OCMStrictClassMock([FullscreenSigninScreenCoordinator class]);
     OCMExpect([(id)fullscreen_signin_screen_coordinator_mock_ alloc])
         .andReturn(fullscreen_signin_screen_coordinator_mock_);
-    ChangeProfileContinuationProvider* provider =
-        static_cast<base::RepeatingCallback<ChangeProfileContinuation()>*>(
-            [OCMArg anyPointer]);
+    ChangeProfileContinuationProvider* provider = ios::OCM::AnyPointer<
+        base::RepeatingCallback<ChangeProfileContinuation()>>();
     OCMExpect(
         [fullscreen_signin_screen_coordinator_mock_
              initWithBaseNavigationController:[OCMArg any]
@@ -182,9 +182,10 @@
     return window_.rootViewController.presentedViewController;
   }
 
-  // Expects no preferences or metrics related to upgrade promo since the access
-  // point is not `kSigninPromo`.
-  void ExpectNoUpgradePromoHistogram(base::HistogramTester* histogram_tester) {
+  // Expects no preferences or metrics related to fullscreen sign-in promo since
+  // the access point is not `kSigninPromo`.
+  void ExpectNoFullscreenSigninPromoHistogram(
+      base::HistogramTester* histogram_tester) {
     histogram_tester->ExpectTotalCount(kUMASSORecallAccountsAvailable, 0);
     histogram_tester->ExpectTotalCount(kUMASSORecallPromoSeenCount, 0);
     histogram_tester->ExpectTotalCount(kUMASSORecallPromoAction, 0);
@@ -267,7 +268,7 @@
   // Expect completion block not to be run when the stop comes from an external
   // caller.
   EXPECT_FALSE(completion_block_done_);
-  ExpectNoUpgradePromoHistogram(&histogram_tester);
+  ExpectNoFullscreenSigninPromoHistogram(&histogram_tester);
   EXPECT_FALSE(scene_state_.signinInProgress);
 }
 
@@ -289,7 +290,7 @@
   // calling -stop. Since the user has already signed in and history sync
   // opt-in, the coordinator will call the completion block.
   EXPECT_TRUE(completion_block_done_);
-  ExpectNoUpgradePromoHistogram(&histogram_tester);
+  ExpectNoFullscreenSigninPromoHistogram(&histogram_tester);
   EXPECT_FALSE(scene_state_.signinInProgress);
 }
 
@@ -306,7 +307,7 @@
   // caller.
   EXPECT_FALSE(completion_block_done_);
 
-  ExpectNoUpgradePromoHistogram(&histogram_tester);
+  ExpectNoFullscreenSigninPromoHistogram(&histogram_tester);
   EXPECT_FALSE(scene_state_.signinInProgress);
 }
 
@@ -325,7 +326,7 @@
   };
   ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
       base::Seconds(1), true, completion_condition));
-  ExpectNoUpgradePromoHistogram(&histogram_tester);
+  ExpectNoFullscreenSigninPromoHistogram(&histogram_tester);
   EXPECT_FALSE(scene_state_.signinInProgress);
 }
 
@@ -351,6 +352,6 @@
       base::Seconds(1), true, completion_condition));
   EXPECT_EQ(1, user_actions_.GetActionCount("Signin_TwoScreens_SwipeDismiss"));
 
-  ExpectNoUpgradePromoHistogram(&histogram_tester);
+  ExpectNoFullscreenSigninPromoHistogram(&histogram_tester);
   EXPECT_FALSE(scene_state_.signinInProgress);
 }
diff --git a/ios/chrome/browser/autofill/model/form_structure_browsertest.mm b/ios/chrome/browser/autofill/model/form_structure_browsertest.mm
index 83e6c1e9..e76f44e 100644
--- a/ios/chrome/browser/autofill/model/form_structure_browsertest.mm
+++ b/ios/chrome/browser/autofill/model/form_structure_browsertest.mm
@@ -205,7 +205,6 @@
           // TODO(crbug.com/40266396): Remove once launched.
           features::kAutofillEnableExpirationDateImprovements,
           features::kAutofillIgnoreCheckableElements,
-          features::kAutofillUnifyRationalizationAndSectioningOrder,
           // TODO(crbug.com/369503318): Remove once launched.
           features::kAutofillSupportSplitZipCode,
       },
diff --git a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm
index cafdce22..0a2c3a2 100644
--- a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm
@@ -261,11 +261,11 @@
 
   FormSuggestionTabHelper* formSuggestionTabHelper =
       FormSuggestionTabHelper::FromWebState(activeWebState);
-  CHECK(formSuggestionTabHelper, base::NotFatalUntil::M137);
+  CHECK(formSuggestionTabHelper);
 
   id<FormInputSuggestionsProvider> crossProvider =
       formSuggestionTabHelper->GetAccessoryViewProvider();
-  CHECK(crossProvider, base::NotFatalUntil::M137);
+  CHECK(crossProvider);
 
   if (crossProvider.type != SuggestionProviderTypeAutofill && !IsV3()) {
     // Last resort safety exit: On the unlikely event that the provider was
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/BUILD.gn b/ios/chrome/browser/autofill/ui_bundled/manual_fill/BUILD.gn
index 8fa581c..c94d46b8 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/BUILD.gn
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/BUILD.gn
@@ -242,6 +242,7 @@
     "//components/password_manager/core/browser:test_support",
     "//components/password_manager/core/browser/features:password_features",
     "//components/prefs",
+    "//components/test/ios",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/affiliations/model",
     "//ios/chrome/browser/autofill/ui_bundled:bridges",
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/form_observer_helper_unittest.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/form_observer_helper_unittest.mm
index d95bbc38..11b0d9e8 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/form_observer_helper_unittest.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/form_observer_helper_unittest.mm
@@ -7,6 +7,7 @@
 #import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
 #import "components/autofill/ios/form_util/form_activity_params.h"
 #import "components/autofill/ios/form_util/test_form_activity_tab_helper.h"
+#import "components/test/ios/test_utils.h"
 #import "ios/chrome/browser/shared/model/web_state_list/test/fake_web_state_list_delegate.h"
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_opener.h"
@@ -74,11 +75,9 @@
   params.value = "value";
   params.input_missing = false;
 
-  OCMExpect([mock_delegate_
-                     webState:static_cast<web::WebState*>([OCMArg anyPointer])
-      didRegisterFormActivity:params
-                      inFrame:static_cast<web::WebFrame*>(
-                                  [OCMArg anyPointer])]);
+  OCMExpect([mock_delegate_ webState:ios::OCM::AnyPointer<web::WebState>()
+             didRegisterFormActivity:params
+                             inFrame:ios::OCM::AnyPointer<web::WebFrame>()]);
 
   autofill::TestFormActivityTabHelper test_form_activity_tab_helper(
       web_state_list_.GetActiveWebState());
@@ -107,31 +106,25 @@
   params.value = "value";
   params.input_missing = false;
 
-  OCMExpect([mock_delegate_
-                     webState:static_cast<web::WebState*>([OCMArg anyPointer])
-      didRegisterFormActivity:params
-                      inFrame:static_cast<web::WebFrame*>(
-                                  [OCMArg anyPointer])]);
+  OCMExpect([mock_delegate_ webState:ios::OCM::AnyPointer<web::WebState>()
+             didRegisterFormActivity:params
+                             inFrame:ios::OCM::AnyPointer<web::WebFrame>()]);
 
   test_form_activity_tab_helper0.FormActivityRegistered(nullptr, params);
   EXPECT_OCMOCK_VERIFY(mock_delegate_);
 
   web_state_list_.ActivateWebStateAt(1);
 
-  OCMExpect([mock_delegate_
-                     webState:static_cast<web::WebState*>([OCMArg anyPointer])
-      didRegisterFormActivity:params
-                      inFrame:static_cast<web::WebFrame*>(
-                                  [OCMArg anyPointer])]);
+  OCMExpect([mock_delegate_ webState:ios::OCM::AnyPointer<web::WebState>()
+             didRegisterFormActivity:params
+                             inFrame:ios::OCM::AnyPointer<web::WebFrame>()]);
   test_form_activity_tab_helper1.FormActivityRegistered(nullptr, params);
   EXPECT_OCMOCK_VERIFY(mock_delegate_);
 
   web_state_list_.ActivateWebStateAt(0);
-  OCMExpect([mock_delegate_
-                     webState:static_cast<web::WebState*>([OCMArg anyPointer])
-      didRegisterFormActivity:params
-                      inFrame:static_cast<web::WebFrame*>(
-                                  [OCMArg anyPointer])]);
+  OCMExpect([mock_delegate_ webState:ios::OCM::AnyPointer<web::WebState>()
+             didRegisterFormActivity:params
+                             inFrame:ios::OCM::AnyPointer<web::WebFrame>()]);
   test_form_activity_tab_helper0.FormActivityRegistered(nullptr, params);
   EXPECT_OCMOCK_VERIFY(mock_delegate_);
 }
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_injection_handler.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_injection_handler.mm
index 94db18d..9d62ab7 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_injection_handler.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_injection_handler.mm
@@ -227,7 +227,7 @@
     // It is really odd to not have params here as getting a suggestion for the
     // manual fallback should correlate with a form activity. Only
     // crash when stateless is enabled so we don't perturbate the current flow.
-    CHECK(_lastFocusedElementParams, base::NotFatalUntil::M137);
+    CHECK(_lastFocusedElementParams);
 
     // Do not pass the params yet as the client will wrap its own params around
     // the suggestion. This is to keep the status quo of how params are handled
diff --git a/ios/chrome/browser/browser_container/ui_bundled/browser_edit_menu_handler_unittest.mm b/ios/chrome/browser/browser_container/ui_bundled/browser_edit_menu_handler_unittest.mm
index 61e7e03b..4b60a4ec 100644
--- a/ios/chrome/browser/browser_container/ui_bundled/browser_edit_menu_handler_unittest.mm
+++ b/ios/chrome/browser/browser_container/ui_bundled/browser_edit_menu_handler_unittest.mm
@@ -198,6 +198,45 @@
       @"0:m:com.apple.menu.share",
       @"1:c:share:"
     ]];
+  } else if (@available(iOS 17, *)) {
+    return [NSMutableArray arrayWithArray:@[
+      @"0:m:com.apple.menu.standard-edit",
+      @"1:c:cut:",
+      @"1:c:copy:",
+      @"1:c:paste:",
+      @"1:c:delete:",
+      @"1:c:select:",
+      @"1:c:selectAll:",
+      @"0:m:com.apple.menu.replace",
+      @"1:c:_promptForReplace:",
+      @"1:c:_transliterateChinese:",
+      @"1:c:_insertDrawing:",
+      @"1:m:com.apple.menu.autofill",
+      @"2:m:com.apple.menu.insert-from-external-sources",
+      @"2:c:captureTextFromCamera:",
+      @"0:m:com.apple.menu.open",
+      @"0:m:com.apple.menu.format",
+      @"1:m:com.apple.menu.text-style",
+      @"2:c:toggleBoldface:",
+      @"2:c:toggleItalics:",
+      @"2:c:toggleUnderline:",
+      @"1:m:com.apple.menu.writing-direction",
+      @"2:c:makeTextWritingDirectionRightToLeft:",
+      @"2:c:makeTextWritingDirectionLeftToRight:",
+      @"1:c:_showTextFormattingOptions:",
+      @"0:m:com.apple.menu.lookup",
+      @"1:c:_findSelected:",
+      @"1:c:_define:",
+      @"1:c:_translate:",
+      @"0:m:com.apple.menu.learn",
+      @"1:c:_addShortcut:",
+      @"0:m:com.apple.command.speech",
+      @"1:c:_accessibilitySpeak:",
+      @"1:c:_accessibilitySpeakLanguageSelection:",
+      @"1:c:_accessibilityPauseSpeaking:",
+      @"0:m:com.apple.menu.share",
+      @"1:c:_share:"
+    ]];
   }
   NSMutableArray* expectedMenuDescription = [NSMutableArray arrayWithArray:@[
     @"0:m:com.apple.menu.standard-edit",
@@ -211,9 +250,7 @@
     @"1:c:_promptForReplace:",
     @"1:c:_transliterateChinese:",
     @"1:c:_insertDrawing:",
-    @"1:m:com.apple.menu.autofill",
-    @"2:m:com.apple.menu.insert-from-external-sources",
-    @"2:c:captureTextFromCamera:",
+    @"1:c:captureTextFromCamera:",
     @"0:m:com.apple.menu.open",
     @"0:m:com.apple.menu.format",
     @"1:m:com.apple.menu.text-style",
@@ -223,7 +260,6 @@
     @"1:m:com.apple.menu.writing-direction",
     @"2:c:makeTextWritingDirectionRightToLeft:",
     @"2:c:makeTextWritingDirectionLeftToRight:",
-    @"1:c:_showTextFormattingOptions:",
     @"0:m:com.apple.menu.lookup",
     @"1:c:_findSelected:",
     @"1:c:_define:",
@@ -237,7 +273,6 @@
     @"0:m:com.apple.menu.share",
     @"1:c:_share:"
   ]];
-
   return expectedMenuDescription;
 }
 
diff --git a/ios/chrome/browser/browser_view/ui_bundled/BUILD.gn b/ios/chrome/browser/browser_view/ui_bundled/BUILD.gn
index baea42f0..e57bb806 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/browser_view/ui_bundled/BUILD.gn
@@ -388,6 +388,7 @@
     "//ios/chrome/browser/ntp/model:util",
     "//ios/chrome/browser/ntp/ui_bundled:component_factory",
     "//ios/chrome/browser/ntp/ui_bundled:coordinator",
+    "//ios/chrome/browser/ntp/ui_bundled:logo",
     "//ios/chrome/browser/omnibox/model/omnibox_position",
     "//ios/chrome/browser/policy/model:policy_util",
     "//ios/chrome/browser/popup_menu/ui_bundled",
diff --git a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
index b9a7501..382cbf2 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
@@ -236,6 +236,7 @@
 #import "ios/chrome/browser/shared/public/commands/drive_file_picker_commands.h"
 #import "ios/chrome/browser/shared/public/commands/enhanced_calendar_commands.h"
 #import "ios/chrome/browser/shared/public/commands/feed_commands.h"
+#import "ios/chrome/browser/shared/public/commands/file_upload_panel_commands.h"
 #import "ios/chrome/browser/shared/public/commands/find_in_page_commands.h"
 #import "ios/chrome/browser/shared/public/commands/google_one_commands.h"
 #import "ios/chrome/browser/shared/public/commands/help_commands.h"
@@ -377,6 +378,7 @@
     EnhancedCalendarCommands,
     EditMenuBuilder,
     EnterprisePromptCoordinatorDelegate,
+    FileUploadPanelCommands,
     FindInPageCommands,
     FormInputAccessoryCoordinatorNavigator,
     BWGCommands,
@@ -899,6 +901,11 @@
   [self stopSaveToPhotos];
   [self hideSaveToDrive];
   [self hideDriveFilePicker];
+  if (@available(iOS 18.4, *)) {
+    if (base::FeatureList::IsEnabled(kIOSCustomFileUploadMenu)) {
+      [self hideFileUploadPanel];
+    }
+  }
   if (IsDownloadListEnabled()) {
     [self hideDownloadList];
   }
@@ -1187,6 +1194,7 @@
     @protocol(EnhancedCalendarCommands),
     @protocol(FeedCommands),
     @protocol(PromosManagerCommands),
+    @protocol(FileUploadPanelCommands),
     @protocol(FindInPageCommands),
     @protocol(BWGCommands),
     @protocol(ReaderModeCommands),
@@ -1794,6 +1802,11 @@
   _dataControlsDialogCoordinator = nil;
 
   [self hideDriveFilePicker];
+  if (@available(iOS 18.4, *)) {
+    if (base::FeatureList::IsEnabled(kIOSCustomFileUploadMenu)) {
+      [self hideFileUploadPanel];
+    }
+  }
   [self hideContextualSheet];
   [self dismissEditAddressBottomSheet];
   [self dismissLensPromo];
@@ -2898,6 +2911,18 @@
   std::move(deactivateReader).Run();
 }
 
+#pragma mark - FileUploadPanelCommands
+
+- (void)showFileUploadPanel API_AVAILABLE(ios(18.4)) {
+  CHECK(base::FeatureList::IsEnabled(kIOSCustomFileUploadMenu));
+  // TODO(crbug.com/441659098): Start the FileUploadPanelCoordinator.
+}
+
+- (void)hideFileUploadPanel API_AVAILABLE(ios(18.4)) {
+  CHECK(base::FeatureList::IsEnabled(kIOSCustomFileUploadMenu));
+  // TODO(crbug.com/441659098): Stop the FileUploadPanelCoordinator.
+}
+
 #pragma mark - FindInPageCommands
 
 - (void)openFindInPage {
@@ -3153,10 +3178,10 @@
   [self.defaultBrowserGenericPromoCoordinator start];
 }
 
-- (void)showSigninPromo {
+- (void)showFullscreenSigninPromo {
   [HandlerForProtocol(self.dispatcher, ApplicationCommands)
-      showSigninUpgradePromoWithCompletion:^(SigninCoordinatorResult result,
-                                             id<SystemIdentity>) {
+      showFullscreenSigninPromoWithCompletion:^(SigninCoordinatorResult result,
+                                                id<SystemIdentity>) {
         [self.promosManagerCoordinator promoWasDismissed];
       }];
 }
diff --git a/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm b/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm
index fb356b58..6b2a924 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm
@@ -1135,7 +1135,10 @@
       // view controller uses information that it should not know or care about:
       // this BVC is contained and its parent bounds to the full screen.
       launchScreenView.frame = self.parentViewController.view.bounds;
+      [self.parentViewController addChildViewController:launchScreenController];
       [self.parentViewController.view addSubview:launchScreenView];
+      [launchScreenController
+          didMoveToParentViewController:self.parentViewController];
       [launchScreenView setNeedsLayout];
       [launchScreenView layoutIfNeeded];
 
@@ -1398,15 +1401,17 @@
 
   if (initialLayout) {
     // Add the toolbars as child view controllers.
-    [self addChildViewController:self.toolbarCoordinator
-                                     .primaryToolbarViewController];
-    [self addChildViewController:self.toolbarCoordinator
-                                     .secondaryToolbarViewController];
+    UIViewController* primaryToolbarViewController =
+        self.toolbarCoordinator.primaryToolbarViewController;
+    [self addChildViewController:primaryToolbarViewController];
+
+    UIViewController* secondaryToolbarViewController =
+        self.toolbarCoordinator.secondaryToolbarViewController;
+    [self addChildViewController:secondaryToolbarViewController];
 
     // Add the primary toolbar. On iPad, it should be in front of the tab strip
     // because the tab strip slides behind it when showing the thumb strip.
-    UIView* primaryToolbarView =
-        self.toolbarCoordinator.primaryToolbarViewController.view;
+    UIView* primaryToolbarView = primaryToolbarViewController.view;
     if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) {
       if (self.tabStripCoordinator) {
         UIViewController* tabStripViewController =
@@ -1428,10 +1433,12 @@
     } else {
       [self.view addSubview:primaryToolbarView];
     }
-    [self.view insertSubview:self.toolbarCoordinator
-                                 .secondaryToolbarViewController.view
+    [self.view insertSubview:secondaryToolbarViewController.view
                 aboveSubview:primaryToolbarView];
 
+    [primaryToolbarViewController didMoveToParentViewController:self];
+    [secondaryToolbarViewController didMoveToParentViewController:self];
+
     // TODO(crbug.com/40270239): Migrate kContentAreaGuide to LayoutGuideCenter.
     // Add guide kContentAreaGuide to the browser view.
     [self.view
@@ -1486,15 +1493,6 @@
     }
 
     AddSameConstraintsToSides(self.view, contentAreaGuide, contentSides);
-
-    // Complete child UIViewController containment flow now that the views are
-    // finished being added.
-    [self.tabStripCoordinator.viewController
-        didMoveToParentViewController:self];
-    [self.toolbarCoordinator.primaryToolbarViewController
-        didMoveToParentViewController:self];
-    [self.toolbarCoordinator.secondaryToolbarViewController
-        didMoveToParentViewController:self];
   }
 
   // Resize the typing shield to cover the entire browser view and bring it to
diff --git a/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller_unittest.mm b/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller_unittest.mm
index a13784f..5f141ae 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller_unittest.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller_unittest.mm
@@ -38,6 +38,7 @@
 #import "ios/chrome/browser/lens/model/lens_browser_agent.h"
 #import "ios/chrome/browser/metrics/model/tab_usage_recorder_browser_agent.h"
 #import "ios/chrome/browser/ntp/model/new_tab_page_tab_helper.h"
+#import "ios/chrome/browser/ntp/ui_bundled/logo_animation_controller.h"
 #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_component_factory.h"
 #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_position/omnibox_position_browser_agent.h"
@@ -578,3 +579,11 @@
   InsertWebState(std::move(ntp_web_state2));
   EXPECT_OCMOCK_VERIFY(container_view_mock);
 }
+
+// BrowserViewController needs to conform to
+// `<LogoAnimationControllerOwnerOwner>` to support VoiceOver. Related to
+// crbug.com/442767141.
+TEST_F(BrowserViewControllerTest, LogoAnimationControllerOwnerOwner) {
+  EXPECT_TRUE(
+      [bvc_ conformsToProtocol:@protocol(LogoAnimationControllerOwnerOwner)]);
+}
diff --git a/ios/chrome/browser/browser_view/ui_bundled/tab_lifecycle_mediator.mm b/ios/chrome/browser/browser_view/ui_bundled/tab_lifecycle_mediator.mm
index 59ecaa8..f3112ce 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/tab_lifecycle_mediator.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/tab_lifecycle_mediator.mm
@@ -39,6 +39,7 @@
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
 #import "ios/chrome/browser/shared/public/commands/contextual_sheet_commands.h"
 #import "ios/chrome/browser/shared/public/commands/data_controls_commands.h"
+#import "ios/chrome/browser/shared/public/commands/file_upload_panel_commands.h"
 #import "ios/chrome/browser/shared/public/commands/help_commands.h"
 #import "ios/chrome/browser/shared/public/commands/lens_commands.h"
 #import "ios/chrome/browser/shared/public/commands/mini_map_commands.h"
@@ -56,6 +57,7 @@
 #import "ios/chrome/browser/tabs/model/tabs_dependency_installer.h"
 #import "ios/chrome/browser/tabs/model/tabs_dependency_installer_bridge.h"
 #import "ios/chrome/browser/web/model/annotations/annotations_tab_helper.h"
+#import "ios/chrome/browser/web/model/choose_file/choose_file_tab_helper.h"
 #import "ios/chrome/browser/web/model/print/print_tab_helper.h"
 #import "ios/chrome/browser/web/model/repost_form_tab_helper.h"
 #import "ios/chrome/browser/web/model/repost_form_tab_helper_delegate.h"
@@ -298,6 +300,14 @@
         FullscreenController::FromBrowser(self.browser);
     findTabHelper->SetFullscreenController(fullscreenController);
   }
+
+  if (base::FeatureList::IsEnabled(kIOSCustomFileUploadMenu)) {
+    ChooseFileTabHelper* chooseFileTabHelper =
+        ChooseFileTabHelper::FromWebState(webState);
+    CHECK(chooseFileTabHelper);
+    chooseFileTabHelper->SetFileUploadPanelHandler(
+        HandlerForProtocol(_commandDispatcher, FileUploadPanelCommands));
+  }
 }
 
 - (void)webStateRemoved:(web::WebState*)webState {
@@ -428,6 +438,13 @@
   if (findTabHelper) {
     findTabHelper->SetFullscreenController(nullptr);
   }
+
+  if (base::FeatureList::IsEnabled(kIOSCustomFileUploadMenu)) {
+    ChooseFileTabHelper* chooseFileTabHelper =
+        ChooseFileTabHelper::FromWebState(webState);
+    CHECK(chooseFileTabHelper);
+    chooseFileTabHelper->SetFileUploadPanelHandler(nil);
+  }
 }
 
 - (void)webStateDeleted:(web::WebState*)webState {
diff --git a/ios/chrome/browser/content_notification/model/content_notification_util.mm b/ios/chrome/browser/content_notification/model/content_notification_util.mm
index e84ffb98..f168e174 100644
--- a/ios/chrome/browser/content_notification/model/content_notification_util.mm
+++ b/ios/chrome/browser/content_notification/model/content_notification_util.mm
@@ -173,10 +173,6 @@
     return false;
   }
 
-  if (!IsContentNotificationExperimentEnabled()) {
-    return false;
-  }
-
   BOOL user_signed_in = IsProfileSignedIn(profile);
 
   if (!ios::TemplateURLServiceFactory::GetForProfile(profile)) {
@@ -204,10 +200,6 @@
     return false;
   }
 
-  if (!IsContentNotificationExperimentEnabled()) {
-    return false;
-  }
-
   BOOL user_signed_in = IsProfileSignedIn(profile);
 
   if (!ios::TemplateURLServiceFactory::GetForProfile(profile)) {
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/BUILD.gn b/ios/chrome/browser/content_suggestions/ui_bundled/BUILD.gn
index 512ec256..965a5b1 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/BUILD.gn
@@ -430,6 +430,7 @@
     "//components/feed/core/v2/public/ios:feed_ios_public",
     "//components/omnibox/browser:aim_eligibility_service_features",
     "//components/regional_capabilities",
+    "//components/safety_check:pref_names",
     "//components/search_engines",
     "//components/segmentation_platform/public",
     "//components/signin/internal/identity_manager",
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/ntp_home_egtest.mm b/ios/chrome/browser/content_suggestions/ui_bundled/ntp_home_egtest.mm
index 3205893..7e01989 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/ntp_home_egtest.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/ntp_home_egtest.mm
@@ -12,6 +12,7 @@
 #import "components/feed/core/v2/public/ios/pref_names.h"
 #import "components/omnibox/browser/aim_eligibility_service_features.h"
 #import "components/regional_capabilities/regional_capabilities_switches.h"
+#import "components/safety_check/safety_check_pref_names.h"
 #import "components/search_engines/search_engines_switches.h"
 #import "components/segmentation_platform/public/features.h"
 #import "components/signin/internal/identity_manager/account_capabilities_constants.h"
@@ -1131,7 +1132,7 @@
   // Check error to ensure module visibility in the Magic Stack.
   [ChromeEarlGrey
       setBoolValue:YES
-       forUserPref:prefs::kHomeCustomizationMagicStackSafetyCheckEnabled];
+       forUserPref:safety_check::prefs::kSafetyCheckHomeModuleEnabled];
   [ChromeEarlGrey
          setStringValue:NameForSafetyCheckState(
                             SafeBrowsingSafetyCheckState::kUnsafe)
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/BUILD.gn b/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/BUILD.gn
index 6f117e81..97858a1 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/BUILD.gn
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/BUILD.gn
@@ -29,6 +29,7 @@
     "//components/password_manager/core/browser",
     "//components/pref_registry",
     "//components/prefs/ios",
+    "//components/safety_check:pref_names",
     "//components/version_info",
     "//ios/chrome/app/profile",
     "//ios/chrome/app/strings",
@@ -76,6 +77,7 @@
   ]
   deps = [
     "//components/prefs",
+    "//components/safety_check:pref_names",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/public/features",
   ]
@@ -119,6 +121,7 @@
     ":prefs",
     "//base/test:test_support",
     "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//components/safety_check:pref_names",
     "//components/segmentation_platform/public",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/content_suggestions/ui_bundled:constants",
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/safety_check_magic_stack_mediator.mm b/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/safety_check_magic_stack_mediator.mm
index dba2a6e..7b90fdb 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/safety_check_magic_stack_mediator.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/safety_check_magic_stack_mediator.mm
@@ -11,6 +11,7 @@
 #import "components/pref_registry/pref_registry_syncable.h"
 #import "components/prefs/ios/pref_observer_bridge.h"
 #import "components/prefs/pref_service.h"
+#import "components/safety_check/safety_check_pref_names.h"
 #import "ios/chrome/app/profile/profile_init_stage.h"
 #import "ios/chrome/app/profile/profile_state.h"
 #import "ios/chrome/app/profile/profile_state_observer.h"
@@ -134,7 +135,7 @@
       _userPrefChangeRegistrar.Init(userState);
 
       _prefObserverBridge->ObserveChangesForPreference(
-          prefs::kHomeCustomizationMagicStackSafetyCheckEnabled,
+          safety_check::prefs::kSafetyCheckHomeModuleEnabled,
           &_userPrefChangeRegistrar);
 
       _safetyCheckState = [self initialSafetyCheckState];
@@ -359,9 +360,9 @@
     // has changed.
     [self runningStateChanged:_safetyCheckState.runningState];
   } else if (preferenceName ==
-                 prefs::kHomeCustomizationMagicStackSafetyCheckEnabled &&
+                 safety_check::prefs::kSafetyCheckHomeModuleEnabled &&
              !_userState->GetBoolean(
-                 prefs::kHomeCustomizationMagicStackSafetyCheckEnabled)) {
+                 safety_check::prefs::kSafetyCheckHomeModuleEnabled)) {
     [self.delegate removeSafetyCheckModule];
   }
 }
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/safety_check_prefs.mm b/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/safety_check_prefs.mm
index 7ab61f88..f6e3f6c 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/safety_check_prefs.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/safety_check_prefs.mm
@@ -6,6 +6,7 @@
 
 #import "components/prefs/pref_registry_simple.h"
 #import "components/prefs/pref_service.h"
+#import "components/safety_check/safety_check_pref_names.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 
@@ -19,13 +20,11 @@
 }
 
 bool IsSafetyCheckInMagicStackDisabled(PrefService* prefs) {
-  return !prefs->GetBoolean(
-      prefs::kHomeCustomizationMagicStackSafetyCheckEnabled);
+  return !prefs->GetBoolean(safety_check::prefs::kSafetyCheckHomeModuleEnabled);
 }
 
 void DisableSafetyCheckInMagicStack(PrefService* prefs) {
-  prefs->SetBoolean(prefs::kHomeCustomizationMagicStackSafetyCheckEnabled,
-                    false);
+  prefs->SetBoolean(safety_check::prefs::kSafetyCheckHomeModuleEnabled, false);
 }
 
 }  // namespace safety_check_prefs
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/safety_check_view_egtest.mm b/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/safety_check_view_egtest.mm
index a6a6306c..79d6e5d 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/safety_check_view_egtest.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/safety_check/safety_check_view_egtest.mm
@@ -5,6 +5,7 @@
 #import "base/test/ios/wait_util.h"
 #import "base/time/time.h"
 #import "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#import "components/safety_check/safety_check_pref_names.h"
 #import "components/segmentation_platform/public/features.h"
 #import "ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_constants.h"
 #import "ios/chrome/browser/content_suggestions/ui_bundled/new_tab_page_app_interface.h"
@@ -101,7 +102,7 @@
   // Enable relevant preferences for the test.
   [ChromeEarlGrey
       setBoolValue:YES
-       forUserPref:prefs::kHomeCustomizationMagicStackSafetyCheckEnabled];
+       forUserPref:safety_check::prefs::kSafetyCheckHomeModuleEnabled];
 
   // Intentionally forces a Safety Check error to ensure module visibility in
   // the Magic Stack.
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/tab_resumption/tab_resumption_view.mm b/ios/chrome/browser/content_suggestions/ui_bundled/tab_resumption/tab_resumption_view.mm
index 8bec12d9..74c9b79 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/tab_resumption/tab_resumption_view.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/tab_resumption/tab_resumption_view.mm
@@ -280,12 +280,17 @@
   }
 
   // Resize the salient image.
-  // TODO(crbug.com/411039614): Replace UIGraphicsBeginImageContextWithOptions
-  // with UIGraphicsImageRenderer.
-  UIGraphicsBeginImageContextWithOptions(CGSize(width, height), NO, 0.0);
-  [_item.contentImage drawInRect:CGRectMake(0, 0, width, height)];
-  UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
-  UIGraphicsEndImageContext();
+  UIGraphicsImageRendererFormat* format =
+      [UIGraphicsImageRendererFormat preferredFormat];
+  format.scale = 0.0;
+  format.opaque = NO;
+  UIGraphicsImageRenderer* renderer =
+      [[UIGraphicsImageRenderer alloc] initWithSize:CGSize(width, height)
+                                             format:format];
+  UIImage* scaledImage =
+      [renderer imageWithActions:^(UIGraphicsImageRendererContext* context) {
+        [_item.contentImage drawInRect:CGRectMake(0, 0, width, height)];
+      }];
   [salientView setImage:scaledImage];
 
   salientView.translatesAutoresizingMaskIntoConstraints = NO;
diff --git a/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.mm b/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.mm
index f39b8c5..3464fd9e 100644
--- a/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.mm
+++ b/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.mm
@@ -805,8 +805,13 @@
   }
 
   if (saveToPhotosAvailable) {
-    UIImage* image = DefaultSymbolWithPointSize(kPhotoBadgeArrowDownSymbol,
-                                                kSymbolActionPointSize);
+    UIImage* image;
+    if (@available(iOS 17, *)) {
+      image = DefaultSymbolWithPointSize(kPhotoBadgeArrowDownSymbol,
+                                         kSymbolActionPointSize);
+    } else {
+      image = DefaultSymbolWithPointSize(kPhotoSymbol, kSymbolActionPointSize);
+    }
     UIMenu* saveImageInMenu = [UIMenu
         menuWithTitle:l10n_util::GetNSString(IDS_IOS_TOOLS_MENU_SAVE_IMAGE_IN)
                 image:image
diff --git a/ios/chrome/browser/context_menu/ui_bundled/context_menu_egtest.mm b/ios/chrome/browser/context_menu/ui_bundled/context_menu_egtest.mm
index 037110e..ee4faa1f 100644
--- a/ios/chrome/browser/context_menu/ui_bundled/context_menu_egtest.mm
+++ b/ios/chrome/browser/context_menu/ui_bundled/context_menu_egtest.mm
@@ -634,10 +634,13 @@
       selectElementWithMatcher:ContextMenuItemWithAccessibilityLabelId(
                                    IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB)]
       assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:
-                 ContextMenuItemWithAccessibilityLabelId(
-                     IDS_IOS_CONTENT_CONTEXT_OPENLINKINNEWTABGROUP)]
-      assertWithMatcher:grey_sufficientlyVisible()];
+  if (@available(iOS 17, *)) {
+    // Tab group is only available on iOS 17+ on iPad.
+    [[EarlGrey selectElementWithMatcher:
+                   ContextMenuItemWithAccessibilityLabelId(
+                       IDS_IOS_CONTENT_CONTEXT_OPENLINKINNEWTABGROUP)]
+        assertWithMatcher:grey_sufficientlyVisible()];
+  }
   [[EarlGrey
       selectElementWithMatcher:ContextMenuItemWithAccessibilityLabelId(
                                    IDS_IOS_OPEN_IN_INCOGNITO_ACTION_TITLE)]
@@ -815,6 +818,10 @@
 // exists in the tab grid, the option `Open in group` will become a submenu,
 // tapping it will result in listing all the existing tab groups.
 - (void)testContextMenuOpenInGroup {
+  if (@available(iOS 17, *)) {
+  } else if ([ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_SKIPPED(@"Only available on iOS 17+ on iPad.");
+  }
   const GURL initialURL = self.testServer->GetURL(kInitialPageUrl);
   [ChromeEarlGrey loadURL:initialURL];
   [ChromeEarlGrey
diff --git a/ios/chrome/browser/crash_report/model/crash_reporter_url_observer.mm b/ios/chrome/browser/crash_report/model/crash_reporter_url_observer.mm
index 2de5db55..8e7ae0f 100644
--- a/ios/chrome/browser/crash_report/model/crash_reporter_url_observer.mm
+++ b/ios/chrome/browser/crash_report/model/crash_reporter_url_observer.mm
@@ -112,7 +112,12 @@
   }
 }
 
-CrashReporterURLObserver::~CrashReporterURLObserver() {}
+CrashReporterURLObserver::~CrashReporterURLObserver() {
+  CHECK(!WebStateListObserver::IsInObserverList() &&
+        !web::WebStateObserver::IsInObserverList())
+      << "CrashReporterURLObserver needs to be removed from "
+         "observer lists before their destruction.";
+}
 
 #pragma mark - Group operations
 
diff --git a/ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_view_controller.mm b/ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_view_controller.mm
index 09bfcbb..f37131b6 100644
--- a/ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_view_controller.mm
+++ b/ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_view_controller.mm
@@ -114,7 +114,7 @@
   LottieAnimationConfiguration* config =
       [[LottieAnimationConfiguration alloc] init];
   config.animationName = animationAssetName;
-  config.loopAnimationCount = 1000;
+  config.shouldLoop = YES;
   return ios::provider::GenerateLottieAnimation(config);
 }
 
diff --git a/ios/chrome/browser/default_promo/ui_bundled/default_browser_instructions_view_controller.mm b/ios/chrome/browser/default_promo/ui_bundled/default_browser_instructions_view_controller.mm
index d4295bc..ce692bb 100644
--- a/ios/chrome/browser/default_promo/ui_bundled/default_browser_instructions_view_controller.mm
+++ b/ios/chrome/browser/default_promo/ui_bundled/default_browser_instructions_view_controller.mm
@@ -207,7 +207,7 @@
   LottieAnimationConfiguration* config =
       [[LottieAnimationConfiguration alloc] init];
   config.animationName = animationAssetName;
-  config.loopAnimationCount = -1;  // Always loop.
+  config.shouldLoop = YES;
   return ios::provider::GenerateLottieAnimation(config);
 }
 
diff --git a/ios/chrome/browser/default_promo/ui_bundled/promo_handler/default_browser_off_cycle_promo_display_handler_unittest.mm b/ios/chrome/browser/default_promo/ui_bundled/promo_handler/default_browser_off_cycle_promo_display_handler_unittest.mm
index c0a2854..0da255a 100644
--- a/ios/chrome/browser/default_promo/ui_bundled/promo_handler/default_browser_off_cycle_promo_display_handler_unittest.mm
+++ b/ios/chrome/browser/default_promo/ui_bundled/promo_handler/default_browser_off_cycle_promo_display_handler_unittest.mm
@@ -46,7 +46,7 @@
 - (void)showOmniboxPositionChoicePromo {
 }
 
-- (void)showSigninPromo {
+- (void)showFullscreenSigninPromo {
 }
 
 - (void)showWelcomeBackPromo {
diff --git a/ios/chrome/browser/default_promo/ui_bundled/promo_handler/default_browser_remind_me_later_promo_display_handler_unittest.mm b/ios/chrome/browser/default_promo/ui_bundled/promo_handler/default_browser_remind_me_later_promo_display_handler_unittest.mm
index 434c549..a2606ae 100644
--- a/ios/chrome/browser/default_promo/ui_bundled/promo_handler/default_browser_remind_me_later_promo_display_handler_unittest.mm
+++ b/ios/chrome/browser/default_promo/ui_bundled/promo_handler/default_browser_remind_me_later_promo_display_handler_unittest.mm
@@ -46,7 +46,7 @@
 - (void)showOmniboxPositionChoicePromo {
 }
 
-- (void)showSigninPromo {
+- (void)showFullscreenSigninPromo {
 }
 
 - (void)showWelcomeBackPromo {
diff --git a/ios/chrome/browser/discover_feed/model/discover_feed_app_agent_profile_helper.mm b/ios/chrome/browser/discover_feed/model/discover_feed_app_agent_profile_helper.mm
index d9dc496..b0db9239 100644
--- a/ios/chrome/browser/discover_feed/model/discover_feed_app_agent_profile_helper.mm
+++ b/ios/chrome/browser/discover_feed/model/discover_feed_app_agent_profile_helper.mm
@@ -117,10 +117,7 @@
     }
   }
 
-  // Only start doing the content notification user eligibility check if
-  // the content notification experiment is enabled.
-  if (IsContentNotificationExperimentEnabled() &&
-      IsContentNotificationProvisionalEnabled(
+  if (IsContentNotificationProvisionalEnabled(
           isUserSignedIn, IsGoogleDefaultSearchEngine(profile),
           profile->GetPrefs())) {
     // This method does not show an UI prompt to the user as provisional
diff --git a/ios/chrome/browser/enterprise/data_controls/model/ios_rules_service_unittest.mm b/ios/chrome/browser/enterprise/data_controls/model/ios_rules_service_unittest.mm
index e28352f..2d6837b 100644
--- a/ios/chrome/browser/enterprise/data_controls/model/ios_rules_service_unittest.mm
+++ b/ios/chrome/browser/enterprise/data_controls/model/ios_rules_service_unittest.mm
@@ -19,7 +19,10 @@
 
 namespace {
 
-constexpr size_t kFirstRuleIndex = 0;
+constexpr Verdict::TriggeredRuleKey kFirstRuleIndex = {
+    .index = 0,
+    .machine_scope = true,
+};
 constexpr char kFirstRuleID[] = "1234";
 
 class IOSRulesServiceTest : public PlatformTest {
diff --git a/ios/chrome/browser/feature_engagement/model/event_exporter_unittest.mm b/ios/chrome/browser/feature_engagement/model/event_exporter_unittest.mm
index b97e5e7..77b3088 100644
--- a/ios/chrome/browser/feature_engagement/model/event_exporter_unittest.mm
+++ b/ios/chrome/browser/feature_engagement/model/event_exporter_unittest.mm
@@ -485,9 +485,9 @@
   FakeSystemIdentityManager::FromSystemIdentityManager(
       GetApplicationContext()->GetSystemIdentityManager())
       ->AddIdentity(fake_identity1);
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
-  signin::RecordUpgradePromoSigninStarted(
+  signin::RecordFullscreenSigninPromoStarted(
       identity_manager_, account_manager_service_, version_1_0);
 
   RequestExportEventsAndVerifyCallback();
diff --git a/ios/chrome/browser/first_run/ui_bundled/animated_lens/ui/animated_lens_promo_view_controller.mm b/ios/chrome/browser/first_run/ui_bundled/animated_lens/ui/animated_lens_promo_view_controller.mm
index b6b33790..7f79bbb 100644
--- a/ios/chrome/browser/first_run/ui_bundled/animated_lens/ui/animated_lens_promo_view_controller.mm
+++ b/ios/chrome/browser/first_run/ui_bundled/animated_lens/ui/animated_lens_promo_view_controller.mm
@@ -84,7 +84,7 @@
   LottieAnimationConfiguration* config =
       [[LottieAnimationConfiguration alloc] init];
   config.animationName = animationAssetName;
-  config.loopAnimationCount = -1;  // Always loop.
+  config.shouldLoop = YES;
   return ios::provider::GenerateLottieAnimation(config);
 }
 
diff --git a/ios/chrome/browser/first_run/ui_bundled/best_features/ui/feature_highlight_screenshot_view_controller.mm b/ios/chrome/browser/first_run/ui_bundled/best_features/ui/feature_highlight_screenshot_view_controller.mm
index e9c832f..c5c64106 100644
--- a/ios/chrome/browser/first_run/ui_bundled/best_features/ui/feature_highlight_screenshot_view_controller.mm
+++ b/ios/chrome/browser/first_run/ui_bundled/best_features/ui/feature_highlight_screenshot_view_controller.mm
@@ -199,7 +199,7 @@
   LottieAnimationConfiguration* config =
       [[LottieAnimationConfiguration alloc] init];
   config.animationName = animationAssetName;
-  config.loopAnimationCount = -1;
+  config.shouldLoop = YES;
   return ios::provider::GenerateLottieAnimation(config);
 }
 
diff --git a/ios/chrome/browser/first_run/ui_bundled/default_browser/default_browser_animated_screen_view_controller.mm b/ios/chrome/browser/first_run/ui_bundled/default_browser/default_browser_animated_screen_view_controller.mm
index f47fd18..29a2dfe 100644
--- a/ios/chrome/browser/first_run/ui_bundled/default_browser/default_browser_animated_screen_view_controller.mm
+++ b/ios/chrome/browser/first_run/ui_bundled/default_browser/default_browser_animated_screen_view_controller.mm
@@ -84,8 +84,8 @@
   if (![self.titleText length] || ![self.subtitleText length]) {
     // Sets default promo text if title and subtitle text are not explicitly
     // set.
-    CHECK(![self.titleText length], base::NotFatalUntil::M138);
-    CHECK(![self.subtitleText length], base::NotFatalUntil::M138);
+    CHECK(![self.titleText length]);
+    CHECK(![self.subtitleText length]);
     BOOL usesTabletStrings =
         ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET;
     [self setPromoTitle:
@@ -270,7 +270,7 @@
   LottieAnimationConfiguration* config =
       [[LottieAnimationConfiguration alloc] init];
   config.animationName = animationAssetName;
-  config.loopAnimationCount = -1;  // Always loop.
+  config.shouldLoop = YES;
   return ios::provider::GenerateLottieAnimation(config);
 }
 
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 472c35e..d42bedd 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -1409,6 +1409,23 @@
      std::size(kTaiyakiChoiceScreenSurfaceParamFREOnly), nullptr},
 };
 
+// Tips Notifications alternative strings.
+const FeatureEntry::FeatureParam kTipsNotificationsAlternative1[] = {
+    {kTipsNotificationsAlternativeStringVersion, "1"}};
+const FeatureEntry::FeatureParam kTipsNotificationsAlternative2[] = {
+    {kTipsNotificationsAlternativeStringVersion, "2"}};
+const FeatureEntry::FeatureParam kTipsNotificationsAlternative3[] = {
+    {kTipsNotificationsAlternativeStringVersion, "3"}};
+
+const FeatureEntry::FeatureVariation
+    kTipsNotificationsAlternativeStringVariation[] = {
+        {" - 1", kTipsNotificationsAlternative1,
+         std::size(kTipsNotificationsAlternative1), nullptr},
+        {" - 2", kTipsNotificationsAlternative2,
+         std::size(kTipsNotificationsAlternative2), nullptr},
+        {" - 3", kTipsNotificationsAlternative3,
+         std::size(kTipsNotificationsAlternative3), nullptr}};
+
 // To add a new entry, add to the end of kFeatureEntries. There are four
 // distinct types of entries:
 // . ENABLE_DISABLE_VALUE: entry is either enabled, disabled, or uses the
@@ -1609,10 +1626,6 @@
      flag_descriptions::kWaitThresholdMillisecondsForCapabilitiesApiDescription,
      flags_ui::kOsIos,
      MULTI_VALUE_TYPE(kWaitThresholdMillisecondsForCapabilitiesApiChoices)},
-    {"content-notification-experiment",
-     flag_descriptions::kContentNotificationExperimentName,
-     flag_descriptions::kContentNotificationExperimentDescription,
-     flags_ui::kOsIos, FEATURE_VALUE_TYPE(kContentNotificationExperiment)},
     {"content-notification-provisional-ignore-conditions",
      flag_descriptions::kContentNotificationProvisionalIgnoreConditionsName,
      flag_descriptions::
@@ -2131,10 +2144,6 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(kIOSSoftLock,
                                     kIOSSoftLockVariations,
                                     "IOSSoftLock")},
-    {"separate-profiles-for-managed-accounts",
-     flag_descriptions::kSeparateProfilesForManagedAccountsName,
-     flag_descriptions::kSeparateProfilesForManagedAccountsDescription,
-     flags_ui::kOsIos, FEATURE_VALUE_TYPE(kSeparateProfilesForManagedAccounts)},
     {"tab-resumption-images", flag_descriptions::kTabResumptionImagesName,
      flag_descriptions::kTabResumptionImagesDescription, flags_ui::kOsIos,
      FEATURE_WITH_PARAMS_VALUE_TYPE(kTabResumptionImages,
@@ -2796,6 +2805,9 @@
      flag_descriptions::kAIMPrototypeImmersiveSRPName,
      flag_descriptions::kAIMPrototypeImmersiveSRPDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kAIMPrototypeImmersiveSRP)},
+    {"aim-prototype-tab-picker", flag_descriptions::kAIMPrototypeTabPickerName,
+     flag_descriptions::kAIMPrototypeTabPickerDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(kAIMPrototypeTabPicker)},
     {"ios-custom-file-upload-menu",
      flag_descriptions::kIOSCustomFileUploadMenuName,
      flag_descriptions::kIOSCustomFileUploadMenuDescription, flags_ui::kOsIos,
@@ -2820,7 +2832,17 @@
      flag_descriptions::kCacheIdentityListInChromeName,
      flag_descriptions::kCacheIdentityListInChromeDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(switches::kCacheIdentityListInChrome)},
-};
+    {"show-tab-grid-on-start", flag_descriptions::kShowTabGridOnStartName,
+     flag_descriptions::kShowTabGridOnStartDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(kShowTabGridOnStart)},
+    {"ios-tips-notifications-string-alternatives",
+     flag_descriptions::kIOSTipsNotificationsStringAlternativesName,
+     flag_descriptions::kIOSTipsNotificationsStringAlternativesDescription,
+     flags_ui::kOsIos,
+     FEATURE_WITH_PARAMS_VALUE_TYPE(
+         kIOSTipsNotificationsAlternativeStrings,
+         kTipsNotificationsAlternativeStringVariation,
+         "IOSTipsNotificationsAlternativeStrings")}};
 
 bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
   return false;
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index a2b4ce8..897bb17f 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -26,6 +26,12 @@
 const char kAIMPrototypeImmersiveSRPDescription[] =
     "When enabled, the AIM prototype will open SRPs in an embedded web view.";
 
+const char kAIMPrototypeTabPickerName[] =
+    "Enable the tab picker within the AIM prototype";
+const char kAIMPrototypeTabPickerDescription[] =
+    "When enabled, users are offered the ability to attach multiple tabs in "
+    "the AIM prototype.";
+
 const char kAskGeminiChipName[] = "Ask Gemini Chip";
 const char kAskGeminiChipDescription[] = "Enables the Ask Gemini Chip feature.";
 
@@ -305,11 +311,6 @@
     "including features such as recent activity, dirty dots, and description "
     "action chips.";
 
-const char kContentNotificationExperimentName[] =
-    "Content Notification Experiment";
-const char kContentNotificationExperimentDescription[] =
-    "Enable Content Notification Experiment.";
-
 const char kContentNotificationProvisionalIgnoreConditionsName[] =
     "Content Notification Provisional Ignore Conditions";
 const char kContentNotificationProvisionalIgnoreConditionsDescription[] =
@@ -855,6 +856,11 @@
     "Enables potential remediations for startup regressions caused by the "
     "reduction of Bling Start time from 6 hours to 4.";
 
+const char kIOSTipsNotificationsStringAlternativesName[] =
+    "Tips notifications alternative string experiment";
+const char kIOSTipsNotificationsStringAlternativesDescription[] =
+    "Enables different alternative strings for tips notifications";
+
 const char kIOSTrustedVaultNotificationName[] =
     "Enable the trusted vault notification on iOS";
 const char kIOSTrustedVaultNotificationDescription[] =
@@ -1464,12 +1470,6 @@
 const char kSendUmaOverAnyNetworkDescription[] =
     "When enabled, will send UMA data over either WiFi or cellular by default.";
 
-const char kSeparateProfilesForManagedAccountsName[] =
-    "Put each managed account into its own profile";
-const char kSeparateProfilesForManagedAccountsDescription[] =
-    "If enabled, each managed account will be assigned to its own separate "
-    "profile.";
-
 const char kShareExtensionForMultiprofileName[] =
     "Enable Share Extension for multiprofile";
 const char kShareExtensionForMultiprofileDescription[] =
@@ -1503,6 +1503,11 @@
     "Annotates web forms with Autofill field type predictions as placeholder "
     "text.";
 
+const char kShowTabGridOnStartName[] = "Show TabGrid on start";
+const char kShowTabGridOnStartDescription[] =
+    "Show TabGrid on start if the last activation is within a specific time "
+    "interval";
+
 const char kSignInButtonNoAvatarName[] =
     "Display sign-in button without avatar";
 const char kSignInButtonNoAvatarDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 1df60a9..7de29061 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -29,6 +29,9 @@
 extern const char kAIMPrototypeImmersiveSRPName[];
 extern const char kAIMPrototypeImmersiveSRPDescription[];
 
+extern const char kAIMPrototypeTabPickerName[];
+extern const char kAIMPrototypeTabPickerDescription[];
+
 extern const char kAskGeminiChipName[];
 extern const char kAskGeminiChipDescription[];
 
@@ -179,9 +182,6 @@
 extern const char kCollaborationMessagingName[];
 extern const char kCollaborationMessagingDescription[];
 
-extern const char kContentNotificationExperimentName[];
-extern const char kContentNotificationExperimentDescription[];
-
 extern const char kContentNotificationProvisionalIgnoreConditionsName[];
 extern const char kContentNotificationProvisionalIgnoreConditionsDescription[];
 
@@ -512,6 +512,9 @@
 extern const char kIOSProvidesAppNotificationSettingsName[];
 extern const char kIOSProvidesAppNotificationSettingsDescription[];
 
+extern const char kIOSTipsNotificationsStringAlternativesName[];
+extern const char kIOSTipsNotificationsStringAlternativesDescription[];
+
 extern const char
     kLensBlockFetchObjectsInteractionRPCsOnSeparateHandshakeName[];
 extern const char
@@ -864,9 +867,6 @@
 extern const char kSendUmaOverAnyNetwork[];
 extern const char kSendUmaOverAnyNetworkDescription[];
 
-extern const char kSeparateProfilesForManagedAccountsName[];
-extern const char kSeparateProfilesForManagedAccountsDescription[];
-
 extern const char kShareExtensionForMultiprofileName[];
 extern const char kShareExtensionForMultiprofileDescription[];
 
@@ -888,6 +888,9 @@
 extern const char kShowAutofillTypePredictionsName[];
 extern const char kShowAutofillTypePredictionsDescription[];
 
+extern const char kShowTabGridOnStartName[];
+extern const char kShowTabGridOnStartDescription[];
+
 extern const char kSignInButtonNoAvatarName[];
 extern const char kSignInButtonNoAvatarDescription[];
 
diff --git a/ios/chrome/browser/home_customization/coordinator/BUILD.gn b/ios/chrome/browser/home_customization/coordinator/BUILD.gn
index cfb9aa8d..a92d2623 100644
--- a/ios/chrome/browser/home_customization/coordinator/BUILD.gn
+++ b/ios/chrome/browser/home_customization/coordinator/BUILD.gn
@@ -31,6 +31,7 @@
     "//components/image_fetcher/core",
     "//components/image_fetcher/ios",
     "//components/prefs",
+    "//components/safety_check:pref_names",
     "//components/sync/protocol:protocol_gen",
     "//components/themes",
     "//ios/chrome/app/strings",
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.mm
index 0ed70ae..7900256 100644
--- a/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.mm
+++ b/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.mm
@@ -8,6 +8,7 @@
 #import "base/memory/raw_ptr.h"
 #import "components/commerce/core/commerce_feature_list.h"
 #import "components/prefs/pref_service.h"
+#import "components/safety_check/safety_check_pref_names.h"
 #import "ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/utils.h"
 #import "ios/chrome/browser/discover_feed/model/discover_feed_visibility_browser_agent.h"
 #import "ios/chrome/browser/discover_feed/model/feed_constants.h"
@@ -111,7 +112,7 @@
   switch (type) {
     case CustomizationToggleType::kSafetyCheck:
       return _prefService->GetBoolean(
-          prefs::kHomeCustomizationMagicStackSafetyCheckEnabled);
+          safety_check::prefs::kSafetyCheckHomeModuleEnabled);
     case CustomizationToggleType::kTapResumption:
       return _prefService->GetBoolean(
           prefs::kHomeCustomizationMagicStackTabResumptionEnabled);
@@ -149,7 +150,7 @@
     // Magic Stack page toggles.
     case CustomizationToggleType::kSafetyCheck:
       _prefService->SetBoolean(
-          prefs::kHomeCustomizationMagicStackSafetyCheckEnabled, enabled);
+          safety_check::prefs::kSafetyCheckHomeModuleEnabled, enabled);
       break;
     case CustomizationToggleType::kTapResumption:
       _prefService->SetBoolean(
diff --git a/ios/chrome/browser/intelligence/bwg/ui/bwg_fre_wrapper_view_controller.mm b/ios/chrome/browser/intelligence/bwg/ui/bwg_fre_wrapper_view_controller.mm
index 9c563fb6..8d3502bf 100644
--- a/ios/chrome/browser/intelligence/bwg/ui/bwg_fre_wrapper_view_controller.mm
+++ b/ios/chrome/browser/intelligence/bwg/ui/bwg_fre_wrapper_view_controller.mm
@@ -190,7 +190,6 @@
   LottieAnimationConfiguration* configuration =
       [[LottieAnimationConfiguration alloc] init];
   configuration.animationName = JSONName;
-  configuration.loopAnimationCount = 1;
 
   id<LottieAnimation> wrapper =
       ios::provider::GenerateLottieAnimation(configuration);
diff --git a/ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_entrypoint_view.mm b/ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_entrypoint_view.mm
index 3aa76a59..e3364078 100644
--- a/ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_entrypoint_view.mm
+++ b/ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_entrypoint_view.mm
@@ -59,7 +59,6 @@
   self = [super initWithFrame:CGRectZero];
   if (self) {
     _backgroundView = [[UIView alloc] init];
-    self.accessibilityIdentifier = kAIHubEntrypointAccessibilityIdentifier;
     self.pointerInteractionEnabled = YES;
     self.minimumDiameter = kMinimumWidth;
     self.pointerStyleProvider = CreateDefaultEffectCirclePointerStyleProvider();
@@ -68,9 +67,12 @@
     if (IsDirectBWGEntryPoint()) {
       self.accessibilityLabel =
           l10n_util::GetNSString(IDS_IOS_BWG_ASK_GEMINI_ACCESSIBILITY_LABEL);
+      self.accessibilityIdentifier =
+          kGeminiDirectEntryPointAccessibilityIdentifier;
     } else {
       self.accessibilityLabel = l10n_util::GetNSString(
           IDS_IOS_BWG_PAGE_ACTION_MENU_ENTRY_POINT_ACCESSIBILITY_LABEL);
+      self.accessibilityIdentifier = kAIHubEntrypointAccessibilityIdentifier;
     }
 
     [self createBackgroundView];
diff --git a/ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_view_controller.mm b/ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_view_controller.mm
index 7e48a8fc..9146d63a 100644
--- a/ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_view_controller.mm
+++ b/ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_view_controller.mm
@@ -222,6 +222,8 @@
       initWithBarButtonSystemItem:UIBarButtonSystemItemClose
                            target:self
                            action:@selector(dismissPageActionMenu)];
+  dismissButton.accessibilityIdentifier =
+      kAIHubDismissButtonAccessibilityIdentifier;
   self.navigationItem.rightBarButtonItem = dismissButton;
 }
 
@@ -402,7 +404,8 @@
                           title:l10n_util::GetNSString(
                                     IDS_IOS_AI_HUB_LENS_LABEL)
                         enabled:[self.mutator isLensAvailableForTraitCollection:
-                                                  self.traitCollection]];
+                                                  self.traitCollection]
+        accessibilityIdentifier:kAIHubLensButtonAccessibilityIdentifier];
   [_lensButton addTarget:self
                   action:@selector(handleLensEntryPointTapped:)
         forControlEvents:UIControlEventTouchUpInside];
@@ -418,17 +421,20 @@
     UIButton* readerModeButton =
         [self createSmallButtonWithIcon:readerModeImage
                                   title:readerModeLabelText
-                                enabled:[self.mutator isReaderModeAvailable]];
+                                enabled:[self.mutator isReaderModeAvailable]
+                accessibilityIdentifier:
+                    kAIHubReaderModeButtonAccessibilityIdentifier];
     [readerModeButton addTarget:self
                          action:@selector(handleReaderModeTapped:)
                forControlEvents:UIControlEventTouchUpInside];
     [stackView addArrangedSubview:readerModeButton];
   } else {
-    _BWGButton =
-        [self createSmallButtonWithIcon:[self askGeminiIcon]
-                                  title:l10n_util::GetNSString(
-                                            IDS_IOS_AI_HUB_GEMINI_LABEL)
-                                enabled:[self.mutator isGeminiAvailable]];
+    _BWGButton = [self
+        createSmallButtonWithIcon:[self askGeminiIcon]
+                            title:l10n_util::GetNSString(
+                                      IDS_IOS_AI_HUB_GEMINI_LABEL)
+                          enabled:[self.mutator isGeminiAvailable]
+          accessibilityIdentifier:kAIHubAskGeminiButtonAccessibilityIdentifier];
     [_BWGButton addTarget:self
                    action:@selector(handleBWGTapped:)
          forControlEvents:UIControlEventTouchUpInside];
@@ -467,6 +473,7 @@
   button.configuration = buttonConfiguration;
 
   button.translatesAutoresizingMaskIntoConstraints = NO;
+  button.accessibilityIdentifier = kAIHubAskGeminiButtonAccessibilityIdentifier;
   [button addTarget:self
                 action:@selector(handleBWGTapped:)
       forControlEvents:UIControlEventTouchUpInside];
@@ -481,7 +488,8 @@
 // disabled.
 - (UIButton*)createSmallButtonWithIcon:(UIImage*)image
                                  title:(NSString*)title
-                               enabled:(BOOL)enabled {
+                               enabled:(BOOL)enabled
+               accessibilityIdentifier:(NSString*)accessibilityIdentifier {
   // Create the background config.
   UIBackgroundConfiguration* backgroundConfig =
       [UIBackgroundConfiguration clearConfiguration];
@@ -521,6 +529,7 @@
   UIButton* button = [UIButton buttonWithConfiguration:buttonConfiguration
                                          primaryAction:nil];
   button.translatesAutoresizingMaskIntoConstraints = NO;
+  button.accessibilityIdentifier = accessibilityIdentifier;
 
   [self updateButton:button enabled:enabled];
 
diff --git a/ios/chrome/browser/intelligence/page_action_menu/utils/ai_hub_constants.h b/ios/chrome/browser/intelligence/page_action_menu/utils/ai_hub_constants.h
index c289ba44..7478f587 100644
--- a/ios/chrome/browser/intelligence/page_action_menu/utils/ai_hub_constants.h
+++ b/ios/chrome/browser/intelligence/page_action_menu/utils/ai_hub_constants.h
@@ -13,5 +13,15 @@
 extern NSString* const kAIHubBottomSheetAccessibilityIdentifier;
 // The accessibility identifier for the AI hub's entrypoint.
 extern NSString* const kAIHubEntrypointAccessibilityIdentifier;
+// The accessibility identifier for the AI hub's Lens button.
+extern NSString* const kAIHubLensButtonAccessibilityIdentifier;
+// The accessibility identifier for the AI hub's Reader mode button.
+extern NSString* const kAIHubReaderModeButtonAccessibilityIdentifier;
+// The accessibility identifier for the AI hub's "Ask Gemini" button.
+extern NSString* const kAIHubAskGeminiButtonAccessibilityIdentifier;
+// The accessibility identifier for the AI hub's dismiss button.
+extern NSString* const kAIHubDismissButtonAccessibilityIdentifier;
+// The accessibility identifier for the Gemini direct entry point.
+extern NSString* const kGeminiDirectEntryPointAccessibilityIdentifier;
 
 #endif  // IOS_CHROME_BROWSER_INTELLIGENCE_PAGE_ACTION_MENU_UTILS_AI_HUB_CONSTANTS_H_
diff --git a/ios/chrome/browser/intelligence/page_action_menu/utils/ai_hub_constants.mm b/ios/chrome/browser/intelligence/page_action_menu/utils/ai_hub_constants.mm
index cc5033e..b06c7798 100644
--- a/ios/chrome/browser/intelligence/page_action_menu/utils/ai_hub_constants.mm
+++ b/ios/chrome/browser/intelligence/page_action_menu/utils/ai_hub_constants.mm
@@ -9,3 +9,13 @@
     @"kAIHubBottomSheetAccessibilityIdentifier";
 NSString* const kAIHubEntrypointAccessibilityIdentifier =
     @"kAIHubEntrypointAccessibilityIdentifier";
+NSString* const kAIHubLensButtonAccessibilityIdentifier =
+    @"kAIHubLensButtonAccessibilityIdentifier";
+NSString* const kAIHubReaderModeButtonAccessibilityIdentifier =
+    @"kAIHubReaderModeButtonAccessibilityIdentifier";
+NSString* const kAIHubAskGeminiButtonAccessibilityIdentifier =
+    @"kAIHubAskGeminiButtonAccessibilityIdentifier";
+NSString* const kAIHubDismissButtonAccessibilityIdentifier =
+    @"kAIHubDismissButtonAccessibilityIdentifier";
+NSString* const kGeminiDirectEntryPointAccessibilityIdentifier =
+    @"kGeminiDirectEntryPointAccessibilityIdentifier";
diff --git a/ios/chrome/browser/intelligence/persist_tab_context/model/persist_tab_context_browser_agent.h b/ios/chrome/browser/intelligence/persist_tab_context/model/persist_tab_context_browser_agent.h
index 1f70a13..0b58201 100644
--- a/ios/chrome/browser/intelligence/persist_tab_context/model/persist_tab_context_browser_agent.h
+++ b/ios/chrome/browser/intelligence/persist_tab_context/model/persist_tab_context_browser_agent.h
@@ -36,6 +36,28 @@
 
   ~PersistTabContextBrowserAgent() override;
 
+  // Type alias for the result map of GetContextsAsync. Maps each webstate
+  // unique id to an optional unique pointer holding the fetched
+  // page context. A `std::nullopt` value indicates that no context was found
+  // for the given ID.
+  using PageContextMap = std::map<
+      std::string,
+      std::optional<std::unique_ptr<optimization_guide::proto::PageContext>>>;
+
+  // Asynchronously fetches a single page context associated with the given
+  // `webstate_unique_id`.
+  void GetSingleContextAsync(
+      const std::string& webstate_unique_id,
+      base::OnceCallback<void(std::optional<std::unique_ptr<
+                                  optimization_guide::proto::PageContext>>)>
+          callback);
+
+  // Asynchronously fetches multiple page contexts for the provided vector of
+  // `webstate_unique_ids`.
+  void GetMultipleContextsAsync(
+      const std::vector<std::string>& webstate_unique_ids,
+      base::OnceCallback<void(PageContextMap)> callback);
+
   // TabsDependencyInstaller
   void OnWebStateInserted(web::WebState* web_state) override;
   void OnWebStateRemoved(web::WebState* web_state) override;
diff --git a/ios/chrome/browser/intelligence/persist_tab_context/model/persist_tab_context_browser_agent.mm b/ios/chrome/browser/intelligence/persist_tab_context/model/persist_tab_context_browser_agent.mm
index 511f9b8..1b9b004 100644
--- a/ios/chrome/browser/intelligence/persist_tab_context/model/persist_tab_context_browser_agent.mm
+++ b/ios/chrome/browser/intelligence/persist_tab_context/model/persist_tab_context_browser_agent.mm
@@ -76,8 +76,88 @@
   }
 }
 
+// Reads the serialized proto data from the file. Returns std::nullopt on
+// failure.
+std::optional<std::string> ReadFileContents(
+    const base::FilePath& storage_directory_path,
+    const std::string& unique_id) {
+  if (storage_directory_path.empty()) {
+    // TODO(crbug.com/445963646): Add metrics logging to browser agent.
+    return std::nullopt;
+  }
+
+  base::FilePath file_path =
+      GetContextFilePath(storage_directory_path, unique_id);
+  if (!base::PathExists(file_path)) {
+    // TODO(crbug.com/445963646): Add metrics logging to browser agent.
+    return std::nullopt;
+  }
+
+  std::string serialized_data;
+  if (!base::ReadFileToString(file_path, &serialized_data)) {
+    // TODO(crbug.com/445963646): Add metrics logging to browser agent.
+    return std::nullopt;
+  }
+
+  return serialized_data;
+}
+
+// Parses serialized data into a PageContext proto. Returns std::nullopt on
+// failure.
+std::optional<std::unique_ptr<optimization_guide::proto::PageContext>>
+ParsePageContext(const std::string& serialized_data) {
+  auto page_context =
+      std::make_unique<optimization_guide::proto::PageContext>();
+  if (!page_context->ParseFromString(serialized_data)) {
+    // TODO(crbug.com/445963646): Add metrics logging to browser agent.
+    return std::nullopt;
+  }
+
+  return page_context;
+}
+
+// Reads and parses a page context from persistent storage using the given
+// `webstate_unique_id`. The context is expected to be stored in a file named
+// "page_context_<unique_id>.proto" within the profile-specific cache
+// directory.
+std::optional<std::unique_ptr<optimization_guide::proto::PageContext>>
+ReadAndParseContextFromStorage(const base::FilePath& storage_directory_path,
+                               const std::string& webstate_unique_id) {
+  std::optional<std::string> serialized_data =
+      ReadFileContents(storage_directory_path, webstate_unique_id);
+  if (!serialized_data) {
+    return std::nullopt;
+  }
+
+  std::optional<std::unique_ptr<optimization_guide::proto::PageContext>>
+      page_context = ParsePageContext(*serialized_data);
+  if (!page_context) {
+    return std::nullopt;
+  }
+
+  // TODO(crbug.com/445963646): Add metrics logging to browser agent. (time and
+  // success)
+
+  return page_context;
+}
+
+// Performs multiple ReadAndParseContextFromStorage calls.
+PersistTabContextBrowserAgent::PageContextMap DoMultipleContextReads(
+    const base::FilePath& storage_directory_path,
+    const std::vector<std::string>& webstate_unique_ids) {
+  PersistTabContextBrowserAgent::PageContextMap result_map;
+  for (const std::string& unique_id : webstate_unique_ids) {
+    result_map[unique_id] =
+        ReadAndParseContextFromStorage(storage_directory_path, unique_id);
+  }
+
+  return result_map;
+}
+
 }  // namespace
 
+#pragma mark - Public
+
 PersistTabContextBrowserAgent::PersistTabContextBrowserAgent(Browser* browser)
     : BrowserUserData(browser),
       task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
@@ -106,59 +186,31 @@
   web_state_observation_.Reset();
 }
 
-void PersistTabContextBrowserAgent::OnSceneActivationLevelChanged(
-    SceneActivationLevel level) {
-  if (level != SceneActivationLevelBackground) {
-    return;
-  }
-
-  web::WebState* active_web_state = web_state_observation_.GetSource();
-
-  if (active_web_state) {
-    WasHidden(active_web_state);
-  }
-}
-
-void PersistTabContextBrowserAgent::OnPageContextExtracted(
+void PersistTabContextBrowserAgent::GetSingleContextAsync(
     const std::string& webstate_unique_id,
-    PageContextWrapperCallbackResponse response) {
-  if (!response.has_value()) {
-    // TODO(crbug.com/445963646): Add metrics logging to browser agent.
-    return;
-  }
-  // TODO(crbug.com/445963646): Add metrics logging to browser agent.
-
-  std::string serialized_page_context;
-  response.value()->SerializeToString(&serialized_page_context);
-
-  task_runner_->PostTask(
+    base::OnceCallback<void(
+        std::optional<std::unique_ptr<optimization_guide::proto::PageContext>>)>
+        callback) {
+  task_runner_->PostTaskAndReplyWithResult(
       FROM_HERE,
-      base::BindOnce(&WriteContextToStorage, std::move(serialized_page_context),
-                     webstate_unique_id, storage_directory_path_));
+      base::BindOnce(&ReadAndParseContextFromStorage, storage_directory_path_,
+                     webstate_unique_id),
+      std::move(callback));
 }
 
-void PersistTabContextBrowserAgent::WasHidden(web::WebState* web_state) {
-  if (!web_state) {
+void PersistTabContextBrowserAgent::GetMultipleContextsAsync(
+    const std::vector<std::string>& webstate_unique_ids,
+    base::OnceCallback<void(PageContextMap)> callback) {
+  if (webstate_unique_ids.empty()) {
+    std::move(callback).Run({});
     return;
   }
 
-  // Cancel any ongoing page context operation.
-  if (page_context_wrapper_) {
-    page_context_wrapper_ = nil;
-  }
-
-  std::string webstate_unique_id =
-      base::NumberToString(web_state->GetUniqueIdentifier().identifier());
-
-  page_context_wrapper_ = [[PageContextWrapper alloc]
-        initWithWebState:web_state
-      completionCallback:
-          base::BindOnce(&PersistTabContextBrowserAgent::OnPageContextExtracted,
-                         weak_factory_.GetWeakPtr(), webstate_unique_id)];
-  [page_context_wrapper_ setShouldGetAnnotatedPageContent:YES];
-  [page_context_wrapper_ setShouldGetInnerText:YES];
-  [page_context_wrapper_ setIsLowPriorityExtraction:YES];
-  [page_context_wrapper_ populatePageContextFieldsAsync];
+  task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(&DoMultipleContextReads, storage_directory_path_,
+                     webstate_unique_ids),
+      base::BindOnce(std::move(callback)));
 }
 
 void PersistTabContextBrowserAgent::OnWebStateInserted(
@@ -193,3 +245,60 @@
     web_state_observation_.Observe(new_active);
   }
 }
+
+#pragma mark - WebStateObserver
+
+void PersistTabContextBrowserAgent::WasHidden(web::WebState* web_state) {
+  if (!web_state) {
+    return;
+  }
+
+  // Cancel any ongoing page context operation.
+  if (page_context_wrapper_) {
+    page_context_wrapper_ = nil;
+  }
+
+  std::string webstate_unique_id =
+      base::NumberToString(web_state->GetUniqueIdentifier().identifier());
+
+  page_context_wrapper_ = [[PageContextWrapper alloc]
+        initWithWebState:web_state
+      completionCallback:
+          base::BindOnce(&PersistTabContextBrowserAgent::OnPageContextExtracted,
+                         weak_factory_.GetWeakPtr(), webstate_unique_id)];
+  [page_context_wrapper_ setShouldGetAnnotatedPageContent:YES];
+  [page_context_wrapper_ setShouldGetInnerText:YES];
+  [page_context_wrapper_ setIsLowPriorityExtraction:YES];
+  [page_context_wrapper_ populatePageContextFieldsAsync];
+}
+
+#pragma mark - Private
+
+void PersistTabContextBrowserAgent::OnSceneActivationLevelChanged(
+    SceneActivationLevel level) {
+  if (level != SceneActivationLevelBackground) {
+    return;
+  }
+
+  web::WebState* active_web_state = web_state_observation_.GetSource();
+
+  if (active_web_state) {
+    WasHidden(active_web_state);
+  }
+}
+
+void PersistTabContextBrowserAgent::OnPageContextExtracted(
+    const std::string& webstate_unique_id,
+    PageContextWrapperCallbackResponse response) {
+  if (!response.has_value()) {
+    return;
+  }
+
+  std::string serialized_page_context;
+  response.value()->SerializeToString(&serialized_page_context);
+
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&WriteContextToStorage, std::move(serialized_page_context),
+                     webstate_unique_id, storage_directory_path_));
+}
diff --git a/ios/chrome/browser/lens_overlay/model/lens_overlay_pan_tracker.h b/ios/chrome/browser/lens_overlay/model/lens_overlay_pan_tracker.h
index 90390a6..21e8c40 100644
--- a/ios/chrome/browser/lens_overlay/model/lens_overlay_pan_tracker.h
+++ b/ios/chrome/browser/lens_overlay/model/lens_overlay_pan_tracker.h
@@ -18,6 +18,10 @@
 // Whether the user pans the view.
 @property(nonatomic, assign, readonly) BOOL isPanning;
 
+// Whether the gesture recognizer cancels touches in view.
+// Default is `NO`.
+@property(nonatomic) BOOL cancelsTouchesInView;
+
 // Creates a new instance of the pan tracker for a given UIView.
 - (instancetype)initWithView:(UIView*)view;
 
diff --git a/ios/chrome/browser/lens_overlay/model/lens_overlay_pan_tracker.mm b/ios/chrome/browser/lens_overlay/model/lens_overlay_pan_tracker.mm
index 8d56054d1..abfbe7a 100644
--- a/ios/chrome/browser/lens_overlay/model/lens_overlay_pan_tracker.mm
+++ b/ios/chrome/browser/lens_overlay/model/lens_overlay_pan_tracker.mm
@@ -16,11 +16,17 @@
   self = [super init];
   if (self) {
     _view = view;
+    _cancelsTouchesInView = NO;
   }
 
   return self;
 }
 
+- (void)setCancelsTouchesInView:(BOOL)cancelsTouchesInView {
+  _cancelsTouchesInView = cancelsTouchesInView;
+  _panRecognizer.cancelsTouchesInView = cancelsTouchesInView;
+}
+
 - (void)startTracking {
   if (!_view || _panRecognizer) {
     return;
@@ -30,7 +36,7 @@
       [[UIPanGestureRecognizer alloc] initWithTarget:self
                                               action:@selector(handlePan:)];
   _panRecognizer.delegate = self;
-  _panRecognizer.cancelsTouchesInView = NO;
+  _panRecognizer.cancelsTouchesInView = _cancelsTouchesInView;
   [_view addGestureRecognizer:_panRecognizer];
 }
 
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_view_controller.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_view_controller.mm
index a5dd363..19e6bdc 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_view_controller.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_view_controller.mm
@@ -195,6 +195,7 @@
   [self.view layoutIfNeeded];
 
   _panTracker = [[LensOverlayPanTracker alloc] initWithView:_bottomSheet.view];
+  _panTracker.cancelsTouchesInView = YES;
   _panTracker.delegate = self;
 
   CGFloat restHeight = [self bottomSheetRestHeight];
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_consent_view_controller.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_consent_view_controller.mm
index 680aee8c..599f33a 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_consent_view_controller.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_consent_view_controller.mm
@@ -33,8 +33,6 @@
 const CGFloat kPauseButtonBottomPadding = 14;
 // The size of the onboarding symbols.
 const CGFloat kLensOverlayOnboaridingSymbolSize = 22;
-// The value that makes the Lottie animation loop indefinitely.
-const CGFloat kLottieInfiniteLoopFlag = -1;
 // The height of the invariant items of the dialog
 // (e.g. bottom action buttons, the padding).
 const CGFloat kDialogFixedItemsHeight = 160;
@@ -315,7 +313,7 @@
   LottieAnimationConfiguration* config =
       [[LottieAnimationConfiguration alloc] init];
   config.animationName = animationAssetName;
-  config.loopAnimationCount = kLottieInfiniteLoopFlag;
+  config.shouldLoop = YES;
   return ios::provider::GenerateLottieAnimation(config);
 }
 
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_presenter.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_presenter.mm
index 71e2904c..a8786037 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_presenter.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_presenter.mm
@@ -106,7 +106,6 @@
                      constant:insets.top];
   [NSLayoutConstraint activateConstraints:@[ _topConstraint ]];
 
-  [_containerViewController didMoveToParentViewController:_baseViewController];
   _containerViewController.selectionViewController.view.alpha = 1;
 
   if (!animated) {
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.mm
index ed240ff..13c285b 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.mm
@@ -192,6 +192,7 @@
   _bottomSheet.view.translatesAutoresizingMaskIntoConstraints = NO;
   [self addChildViewController:_bottomSheet];
   [self.view addSubview:_bottomSheet.view];
+  [_bottomSheet didMoveToParentViewController:self];
 
   AddSameConstraints(_bottomSheet.view, self.view);
   [_bottomSheet setContent:viewController];
@@ -207,6 +208,7 @@
   _sidePanel.view.translatesAutoresizingMaskIntoConstraints = NO;
   [self addChildViewController:_sidePanel];
   [self.view addSubview:_sidePanel.view];
+  [_sidePanel didMoveToParentViewController:self];
 
   AddSameConstraintsToSides(_sidePanel.view, _splitViewLayoutGuide,
                             (LayoutSides::kTop | LayoutSides::kBottom));
diff --git a/ios/chrome/browser/menu/ui_bundled/menu_histograms.h b/ios/chrome/browser/menu/ui_bundled/menu_histograms.h
index 4ed246a4..2231e9b 100644
--- a/ios/chrome/browser/menu/ui_bundled/menu_histograms.h
+++ b/ios/chrome/browser/menu/ui_bundled/menu_histograms.h
@@ -47,6 +47,7 @@
   kMenuScenarioHistogramReaderModeContextMenuImage = 34,
   kMenuScenarioHistogramReaderModeContextMenuImageLink = 35,
   kMenuScenarioHistogramReaderModeContextMenuLink = 36,
+  kMenuScenarioHistogramTabGroupOverflowMenu = 37,
   kMenuScenarioHistogramCount,
 };
 // LINT.ThenChange(/tools/metrics/histograms/metadata/mobile/enums.xml)
diff --git a/ios/chrome/browser/menu/ui_bundled/menu_histograms.mm b/ios/chrome/browser/menu/ui_bundled/menu_histograms.mm
index 2717b5d..8afa5011 100644
--- a/ios/chrome/browser/menu/ui_bundled/menu_histograms.mm
+++ b/ios/chrome/browser/menu/ui_bundled/menu_histograms.mm
@@ -82,6 +82,8 @@
     "Mobile.ContextMenu.ReaderModeImageLink.Actions";
 const char kContextMenuReaderModeLinkActionsHistogram[] =
     "Mobile.ContextMenu.ReaderModeLink.Actions";
+const char kContextMenuTabGroupOverflowMenuHistogram[] =
+    "Mobile.ContextMenu.TabGroupOverflowMenu.Actions";
 // LINT.ThenChange(/tools/metrics/histograms/metadata/mobile/histograms.xml)
 }  // namespace
 
@@ -164,6 +166,8 @@
       return kContextMenuReaderModeImageLinkActionsHistogram;
     case kMenuScenarioHistogramReaderModeContextMenuLink:
       return kContextMenuReaderModeLinkActionsHistogram;
+    case kMenuScenarioHistogramTabGroupOverflowMenu:
+      return kContextMenuTabGroupOverflowMenuHistogram;
     case kMenuScenarioHistogramCount:
       NOTREACHED();
   }
diff --git a/ios/chrome/browser/ntp/ui_bundled/BUILD.gn b/ios/chrome/browser/ntp/ui_bundled/BUILD.gn
index fc501ec..6c15c16 100644
--- a/ios/chrome/browser/ntp/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/ntp/ui_bundled/BUILD.gn
@@ -132,6 +132,7 @@
     "//components/pref_registry",
     "//components/prefs",
     "//components/prefs/ios",
+    "//components/safety_check:pref_names",
     "//components/search",
     "//components/search_engines",
     "//components/signin/public/base",
diff --git a/ios/chrome/browser/ntp/ui_bundled/feed_top_section/feed_top_section_mediator.mm b/ios/chrome/browser/ntp/ui_bundled/feed_top_section/feed_top_section_mediator.mm
index 5c42dfbd..69dc0437 100644
--- a/ios/chrome/browser/ntp/ui_bundled/feed_top_section/feed_top_section_mediator.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/feed_top_section/feed_top_section_mediator.mm
@@ -238,8 +238,7 @@
     return true;
   }
 
-  if (!IsContentNotificationExperimentEnabled() ||
-      !IsContentNotificationPromoEnabled([self isUserSignedIn],
+  if (!IsContentNotificationPromoEnabled([self isUserSignedIn],
                                          self.isDefaultSearchEngine,
                                          self.prefService)) {
     return false;
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
index 4a718c8..75db6fb0 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
@@ -21,6 +21,7 @@
 #import "components/policy/policy_constants.h"
 #import "components/pref_registry/pref_registry_syncable.h"
 #import "components/prefs/pref_service.h"
+#import "components/safety_check/safety_check_pref_names.h"
 #import "components/search/search.h"
 #import "components/search_engines/template_url_service.h"
 #import "components/signin/public/base/signin_metrics.h"
@@ -1796,7 +1797,7 @@
 
       PrefService* prefService = self.prefService;
       BOOL safetyCheckEnabled = prefService->GetBoolean(
-          prefs::kHomeCustomizationMagicStackSafetyCheckEnabled);
+          safety_check::prefs::kSafetyCheckHomeModuleEnabled);
       BOOL tabResumptionEnabled = prefService->GetBoolean(
           prefs::kHomeCustomizationMagicStackTabResumptionEnabled);
       BOOL tipsEnabled = prefService->GetBoolean(
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm
index 393b7d54..27f38e2 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm
@@ -123,9 +123,6 @@
 NSString* const kMIACircleAnimationLightMode = @"mia_circle_animation_no_glow";
 NSString* const kMIACircleAnimationDarkMode = @"mia_glowing_circle_animation";
 
-// The value that makes the Lottie animation loop indefinitely.
-const CGFloat kLottieInfiniteLoopFlag = -1;
-
 // The value of the sides of the MIA circle animation for the normal size of the
 // fakebox.
 const CGFloat kMIACircleAnimationSizeNormal = 40.0;
@@ -1336,7 +1333,7 @@
       self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark
           ? kMIACircleAnimationDarkMode
           : kMIACircleAnimationLightMode;
-  config.loopAnimationCount = kLottieInfiniteLoopFlag;
+  config.shouldLoop = YES;
   return ios::provider::GenerateLottieAnimation(config);
 }
 
diff --git a/ios/chrome/browser/omnibox/eg_tests/omnibox_popup_egtest.mm b/ios/chrome/browser/omnibox/eg_tests/omnibox_popup_egtest.mm
index 863a363..c203468 100644
--- a/ios/chrome/browser/omnibox/eg_tests/omnibox_popup_egtest.mm
+++ b/ios/chrome/browser/omnibox/eg_tests/omnibox_popup_egtest.mm
@@ -225,9 +225,7 @@
 
 // Test that swiping left on a historical suggestion and tapping
 // the delete button , removes the suggestions.
-// TODO(crbug.com/440119404): Re-enable after roll is complete and testers are
-// also updated.
-- (void)DISABLED_testDeleteHistoricalSuggestion {
+- (void)testDeleteHistoricalSuggestion {
   [self populateHistory];
   NSString* omniboxInput = [NSString
       stringWithFormat:@"%@:%@", [NSString cr_fromString:_URL3.GetHost()],
diff --git a/ios/chrome/browser/omnibox/ui/popup/row/omnibox_popup_row_trailing_button.mm b/ios/chrome/browser/omnibox/ui/popup/row/omnibox_popup_row_trailing_button.mm
index 092765c5..deb3d94 100644
--- a/ios/chrome/browser/omnibox/ui/popup/row/omnibox_popup_row_trailing_button.mm
+++ b/ios/chrome/browser/omnibox/ui/popup/row/omnibox_popup_row_trailing_button.mm
@@ -250,7 +250,7 @@
       self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark
           ? kAIMCircleAnimationDarkMode
           : kAIMCircleAnimationLightMode;
-  config.loopAnimationCount = -1;
+  config.shouldLoop = YES;
 
   _aimAnimation = ios::provider::GenerateLottieAnimation(config);
   return _aimAnimation;
diff --git a/ios/chrome/browser/optimization_guide/model/optimization_guide_service.h b/ios/chrome/browser/optimization_guide/model/optimization_guide_service.h
index 668bbff..426be9d 100644
--- a/ios/chrome/browser/optimization_guide/model/optimization_guide_service.h
+++ b/ios/chrome/browser/optimization_guide/model/optimization_guide_service.h
@@ -148,6 +148,7 @@
   void AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OptimizationTarget optimization_target,
       const std::optional<optimization_guide::proto::Any>& model_metadata,
+      scoped_refptr<base::SequencedTaskRunner> model_task_runner,
       optimization_guide::OptimizationTargetModelObserver* observer) override;
   void RemoveObserverForOptimizationTargetModel(
       optimization_guide::proto::OptimizationTarget optimization_target,
diff --git a/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm b/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm
index e4a3031..4cb5839 100644
--- a/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm
+++ b/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm
@@ -261,10 +261,11 @@
 void OptimizationGuideService::AddObserverForOptimizationTargetModel(
     optimization_guide::proto::OptimizationTarget optimization_target,
     const std::optional<optimization_guide::proto::Any>& model_metadata,
+    scoped_refptr<base::SequencedTaskRunner> model_task_runner,
     optimization_guide::OptimizationTargetModelObserver* observer) {
   if (optimization_guide::features::IsOptimizationTargetPredictionEnabled()) {
     GetPredictionManager()->AddObserverForOptimizationTargetModel(
-        optimization_target, model_metadata, observer);
+        optimization_target, model_metadata, model_task_runner, observer);
   }
 }
 
diff --git a/ios/chrome/browser/optimization_guide/model/prediction_manager_browsertest.mm b/ios/chrome/browser/optimization_guide/model/prediction_manager_browsertest.mm
index 1ccc65f..f610c8f1 100644
--- a/ios/chrome/browser/optimization_guide/model/prediction_manager_browsertest.mm
+++ b/ios/chrome/browser/optimization_guide/model/prediction_manager_browsertest.mm
@@ -6,6 +6,7 @@
 
 #import "base/containers/contains.h"
 #import "base/files/file_util.h"
+#import "base/task/thread_pool.h"
 #import "base/test/metrics/histogram_tester.h"
 #import "base/test/scoped_command_line.h"
 #import "base/test/scoped_feature_list.h"
@@ -163,7 +164,10 @@
     OptimizationGuideServiceFactory::GetForProfile(scoped_profile_.profile())
         ->AddObserverForOptimizationTargetModel(
             optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
-            std::nullopt, model_file_observer);
+            std::nullopt,
+            base::ThreadPool::CreateSequencedTaskRunner(
+                {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
+            model_file_observer);
   }
 
   void SetComponentUpdatesEnabled(bool enabled) {
@@ -358,7 +362,10 @@
         profile ? profile : scoped_profile_.profile())
         ->AddObserverForOptimizationTargetModel(
             optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
-            /*model_metadata=*/std::nullopt, model_file_observer_.get());
+            /*model_metadata=*/std::nullopt,
+            base::ThreadPool::CreateSequencedTaskRunner(
+                {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
+            model_file_observer_.get());
   }
 
   // Sets up the model observer to receive valid ModelInfo.
diff --git a/ios/chrome/browser/overlays/model/BUILD.gn b/ios/chrome/browser/overlays/model/BUILD.gn
index 01009b0..3aa08bc 100644
--- a/ios/chrome/browser/overlays/model/BUILD.gn
+++ b/ios/chrome/browser/overlays/model/BUILD.gn
@@ -61,6 +61,7 @@
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/tabs/model:dependency_installer",
+    "//ios/chrome/browser/tabs/model:features",
     "//ios/web/public",
   ]
 }
diff --git a/ios/chrome/browser/overlays/model/DEPS b/ios/chrome/browser/overlays/model/DEPS
index 6cd62b0..a91ef27 100644
--- a/ios/chrome/browser/overlays/model/DEPS
+++ b/ios/chrome/browser/overlays/model/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+ios/chrome/browser/infobars/model",
+  "+ios/chrome/browser/tabs/model/features.h",
   "+ios/chrome/browser/tabs/model/tabs_dependency_installer.h",
 ]
 
diff --git a/ios/chrome/browser/overlays/model/overlay_presenter_impl.mm b/ios/chrome/browser/overlays/model/overlay_presenter_impl.mm
index e964d56..be2b1c6 100644
--- a/ios/chrome/browser/overlays/model/overlay_presenter_impl.mm
+++ b/ios/chrome/browser/overlays/model/overlay_presenter_impl.mm
@@ -13,6 +13,7 @@
 #import "ios/chrome/browser/overlays/model/public/overlay_request.h"
 #import "ios/chrome/browser/overlays/model/public/overlay_request_support.h"
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
+#import "ios/chrome/browser/tabs/model/features.h"
 
 #pragma mark - Factory method
 
@@ -170,6 +171,11 @@
   if (!web_state) {
     return nullptr;
   }
+  if (CreateTabHelperOnlyForRealizedWebStates()) {
+    if (!web_state->IsRealized()) {
+      return nullptr;
+    }
+  }
   OverlayRequestQueueImpl::Container::CreateForWebState(web_state);
   return OverlayRequestQueueImpl::Container::FromWebState(web_state)
       ->QueueForModality(modality_);
diff --git a/ios/chrome/browser/page_info/ui_bundled/OWNERS b/ios/chrome/browser/page_info/OWNERS
similarity index 69%
rename from ios/chrome/browser/page_info/ui_bundled/OWNERS
rename to ios/chrome/browser/page_info/OWNERS
index 6cadd6b..70c0618 100644
--- a/ios/chrome/browser/page_info/ui_bundled/OWNERS
+++ b/ios/chrome/browser/page_info/OWNERS
@@ -1,2 +1,3 @@
 ewannpv@chromium.org
+fsenra@google.com
 gambard@chromium.org
diff --git a/ios/chrome/browser/page_info/ui_bundled/page_info_view_controller.mm b/ios/chrome/browser/page_info/ui_bundled/page_info_view_controller.mm
index fc5a08db..8655961 100644
--- a/ios/chrome/browser/page_info/ui_bundled/page_info_view_controller.mm
+++ b/ios/chrome/browser/page_info/ui_bundled/page_info_view_controller.mm
@@ -524,6 +524,8 @@
       [cell createDetailTextLabel];
       [cell setDetailTextNumberOfLines:0];
       cell.textLayoutConstraintAxis = UILayoutConstraintAxisVertical;
+      cell.separatorInset =
+          UIEdgeInsetsMake(0, kPageInfoTableViewSeparatorInset, 0, 0);
 
       if (_trackingProtectionInfo.hasTrackingProtectionException) {
         cell.textLabel.text = l10n_util::GetNSString(
diff --git a/ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_factory.h b/ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_factory.h
index 920be7d..4a8eedc 100644
--- a/ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_factory.h
+++ b/ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_factory.h
@@ -18,6 +18,7 @@
   static scoped_refptr<IOSChromePasswordCheckManager> GetForProfile(
       ProfileIOS* profile);
   static IOSChromePasswordCheckManagerFactory* GetInstance();
+  static TestingFactory GetDefaultFactory();
 
  private:
   friend class base::NoDestructor<IOSChromePasswordCheckManagerFactory>;
diff --git a/ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_factory.mm b/ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_factory.mm
index 9185dca..cf63df72 100644
--- a/ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_factory.mm
+++ b/ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_factory.mm
@@ -17,6 +17,22 @@
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/webauthn/model/ios_passkey_model_factory.h"
 
+namespace {
+// Default factory for IOSChromePasswordCheckManager.
+scoped_refptr<RefcountedKeyedService> BuildInstance(ProfileIOS* profile) {
+  return base::MakeRefCounted<IOSChromePasswordCheckManager>(
+      profile->GetPrefs(),
+      IOSChromeBulkLeakCheckServiceFactory::GetForProfile(profile),
+      std::make_unique<password_manager::SavedPasswordsPresenter>(
+          IOSChromeAffiliationServiceFactory::GetForProfile(profile),
+          IOSChromeProfilePasswordStoreFactory::GetForProfile(
+              profile, ServiceAccessType::EXPLICIT_ACCESS),
+          IOSChromeAccountPasswordStoreFactory::GetForProfile(
+              profile, ServiceAccessType::EXPLICIT_ACCESS),
+          IOSPasskeyModelFactory::GetForProfile(profile)));
+}
+}  // namespace
+
 // static
 IOSChromePasswordCheckManagerFactory*
 IOSChromePasswordCheckManagerFactory::GetInstance() {
@@ -46,14 +62,11 @@
 scoped_refptr<RefcountedKeyedService>
 IOSChromePasswordCheckManagerFactory::BuildServiceInstanceFor(
     ProfileIOS* profile) const {
-  return base::MakeRefCounted<IOSChromePasswordCheckManager>(
-      profile->GetPrefs(),
-      IOSChromeBulkLeakCheckServiceFactory::GetForProfile(profile),
-      std::make_unique<password_manager::SavedPasswordsPresenter>(
-          IOSChromeAffiliationServiceFactory::GetForProfile(profile),
-          IOSChromeProfilePasswordStoreFactory::GetForProfile(
-              profile, ServiceAccessType::EXPLICIT_ACCESS),
-          IOSChromeAccountPasswordStoreFactory::GetForProfile(
-              profile, ServiceAccessType::EXPLICIT_ACCESS),
-          IOSPasskeyModelFactory::GetForProfile(profile)));
+  return BuildInstance(profile);
+}
+
+// static
+IOSChromePasswordCheckManagerFactory::TestingFactory
+IOSChromePasswordCheckManagerFactory::GetDefaultFactory() {
+  return base::BindOnce(&BuildInstance);
 }
diff --git a/ios/chrome/browser/phone_number/ui_bundled/country_code_picker_view_controller.mm b/ios/chrome/browser/phone_number/ui_bundled/country_code_picker_view_controller.mm
index 7a5937cf..497aef6 100644
--- a/ios/chrome/browser/phone_number/ui_bundled/country_code_picker_view_controller.mm
+++ b/ios/chrome/browser/phone_number/ui_bundled/country_code_picker_view_controller.mm
@@ -155,9 +155,11 @@
       kCountryCodePickerTableViewIdentifier;
   [self loadModel];
 
-  NSArray<UITrait>* traits =
-      TraitCollectionSetForTraits(@[ UITraitVerticalSizeClass.class ]);
-  [self registerForTraitChanges:traits withAction:@selector(updateTitle)];
+  if (@available(iOS 17, *)) {
+    NSArray<UITrait>* traits =
+        TraitCollectionSetForTraits(@[ UITraitVerticalSizeClass.class ]);
+    [self registerForTraitChanges:traits withAction:@selector(updateTitle)];
+  }
 }
 
 #pragma mark - LegacyChromeTableViewController
@@ -221,6 +223,18 @@
   [self updateTitle];
 }
 
+#pragma mark - UIView
+
+#if !defined(__IPHONE_17_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_17_0
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  if (@available(iOS 17, *)) {
+    return;
+  }
+  [self updateTitle];
+}
+#endif
+
 #pragma mark - UISearchResultsUpdating
 
 - (void)updateSearchResultsForSearchController:
diff --git a/ios/chrome/browser/promos_manager/model/constants.cc b/ios/chrome/browser/promos_manager/model/constants.cc
index 628364a..61d7d05 100644
--- a/ios/chrome/browser/promos_manager/model/constants.cc
+++ b/ios/chrome/browser/promos_manager/model/constants.cc
@@ -80,8 +80,8 @@
     return promos_manager::Promo::PostDefaultAbandonment;
   }
 
-  if (promo == "promos_manager::Promo::SigninFullscreen") {
-    return promos_manager::Promo::SigninFullscreen;
+  if (promo == "promos_manager::Promo::FullscreenSignin") {
+    return promos_manager::Promo::FullscreenSignin;
   }
 
   if (promo == "promos_manager::Promo::WelcomeBack") {
@@ -139,8 +139,8 @@
       return "StaySafeDefaultBrowser";
     case promos_manager::Promo::PostDefaultAbandonment:
       return "PostDefaultAbandonment";
-    case promos_manager::Promo::SigninFullscreen:
-      return "SigninFullscreen";
+    case promos_manager::Promo::FullscreenSignin:
+      return "FullscreenSignin";
     case promos_manager::Promo::WelcomeBack:
       return "WelcomeBack";
     case promos_manager::Promo::BWGPromo:
diff --git a/ios/chrome/browser/promos_manager/model/constants.h b/ios/chrome/browser/promos_manager/model/constants.h
index 2b1c3e7..c19ca2a 100644
--- a/ios/chrome/browser/promos_manager/model/constants.h
+++ b/ios/chrome/browser/promos_manager/model/constants.h
@@ -45,7 +45,7 @@
   MadeForIOSDefaultBrowser = 14,   // "Made For iOS" default browser promo.
   StaySafeDefaultBrowser = 15,     // "Stay Safe" default browser promo.
   PostDefaultAbandonment = 16,     // Post-default browser abandonment alert.
-  SigninFullscreen = 17,           // Sign-in fullscreen promo.
+  FullscreenSignin = 17,           // Fullscreen sign-in promo.
   WelcomeBack = 18,                // Welcome Back promo.
   BWGPromo = 19,                   // BWG promo.
   SafariImportRemindMeLater =
diff --git a/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm b/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm
index 5bdab904..c165122 100644
--- a/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm
+++ b/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm
@@ -23,7 +23,7 @@
 #import "ios/chrome/browser/app_store_rating/ui_bundled/app_store_rating_display_handler.h"
 #import "ios/chrome/browser/app_store_rating/ui_bundled/features.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/features.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_display_handler.h"
+#import "ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_display_handler.h"
 #import "ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_display_handler.h"
 #import "ios/chrome/browser/default_browser/model/utils.h"
 #import "ios/chrome/browser/default_promo/ui_bundled/all_tabs_default_browser_promo_view_provider.h"
@@ -634,8 +634,8 @@
 
   // Sign-in fullscreen promo handler.
   if (IsFullscreenSigninPromoManagerMigrationEnabled()) {
-    _displayHandlerPromos[promos_manager::Promo::SigninFullscreen] =
-        [[SigninFullscreenPromoDisplayHandler alloc] init];
+    _displayHandlerPromos[promos_manager::Promo::FullscreenSignin] =
+        [[FullscreenSigninPromoDisplayHandler alloc] init];
   }
 
   // Welcome Back promo handler.
diff --git a/ios/chrome/browser/push_notification/model/BUILD.gn b/ios/chrome/browser/push_notification/model/BUILD.gn
index 38c6df9..b9f0f8e 100644
--- a/ios/chrome/browser/push_notification/model/BUILD.gn
+++ b/ios/chrome/browser/push_notification/model/BUILD.gn
@@ -242,12 +242,15 @@
     "push_notification_account_context_manager_unittest.mm",
     "push_notification_client_manager_unittest.mm",
     "push_notification_client_unittest.mm",
+    "push_notification_delegate_unittest.mm",
     "push_notification_settings_util_unittest.mm",
     "push_notification_util+testing.h",
     "push_notification_util_unittest.mm",
   ]
   deps = [
     ":constants",
+    ":profile_service",
+    ":profile_service_factory",
     ":push_notification_client",
     ":push_notification_client_id",
     ":push_notification_delegate",
@@ -258,14 +261,24 @@
     ":test_support",
     "//base",
     "//base/test:test_support",
+    "//components/affiliations/core/browser:test_support",
     "//components/commerce/core:pref_names",
     "//components/prefs",
     "//components/sync_preferences:test_support",
     "//google_apis",
+    "//ios/chrome/app/application_delegate:app_state",
+    "//ios/chrome/app/profile",
+    "//ios/chrome/browser/affiliations/model",
+    "//ios/chrome/browser/passwords/model",
+    "//ios/chrome/browser/profile/model:test_support",
+    "//ios/chrome/browser/safety_check/model:factory",
     "//ios/chrome/browser/safety_check_notifications/utils",
     "//ios/chrome/browser/safety_check_notifications/utils:constants",
     "//ios/chrome/browser/send_tab_to_self/model",
+    "//ios/chrome/browser/shared/coordinator/scene/test",
     "//ios/chrome/browser/shared/model/application_context",
+    "//ios/chrome/browser/shared/model/browser",
+    "//ios/chrome/browser/shared/model/browser/test:test_support",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile:features",
diff --git a/ios/chrome/browser/push_notification/model/push_notification_client_manager.mm b/ios/chrome/browser/push_notification/model/push_notification_client_manager.mm
index 7176c88..e6bdc42 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_client_manager.mm
+++ b/ios/chrome/browser/push_notification/model/push_notification_client_manager.mm
@@ -144,10 +144,8 @@
 PushNotificationClientManager::GetClients() {
   std::vector<PushNotificationClientId> client_ids = {
       PushNotificationClientId::kCommerce, PushNotificationClientId::kTips};
-  if (IsContentNotificationExperimentEnabled()) {
-    client_ids.push_back(PushNotificationClientId::kContent);
-    client_ids.push_back(PushNotificationClientId::kSports);
-  }
+  client_ids.push_back(PushNotificationClientId::kContent);
+  client_ids.push_back(PushNotificationClientId::kSports);
   if (IsSafetyCheckNotificationsEnabled()) {
     client_ids.push_back(PushNotificationClientId::kSafetyCheck);
   }
@@ -186,23 +184,22 @@
     AddPushNotificationClient(std::move(client));
   }
 
-  if (IsContentNotificationExperimentEnabled()) {
-    std::unique_ptr<ContentNotificationClient> client;
+  std::unique_ptr<ContentNotificationClient> content_notification_client;
 
-    if (IsMultiProfilePushNotificationHandlingEnabled()) {
-      CHECK(profile_);
+  if (IsMultiProfilePushNotificationHandlingEnabled()) {
+    CHECK(profile_);
 
-      client = std::make_unique<ContentNotificationClient>(profile_);
-    } else {
-      client = std::make_unique<ContentNotificationClient>();
-    }
-
-    CHECK_EQ(client->GetClientScope(),
-             PushNotificationClientScope::kPerProfile);
-
-    AddPushNotificationClient(std::move(client));
+    content_notification_client =
+        std::make_unique<ContentNotificationClient>(profile_);
+  } else {
+    content_notification_client = std::make_unique<ContentNotificationClient>();
   }
 
+  CHECK_EQ(content_notification_client->GetClientScope(),
+           PushNotificationClientScope::kPerProfile);
+
+  AddPushNotificationClient(std::move(content_notification_client));
+
   if (IsSafetyCheckNotificationsEnabled()) {
     if (IsMultiProfilePushNotificationHandlingEnabled() && profile_) {
       // Pass profile and task runner for multi-profile handling.
diff --git a/ios/chrome/browser/push_notification/model/push_notification_delegate.h b/ios/chrome/browser/push_notification/model/push_notification_delegate.h
index 8e3188a1..c1eb9fc 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_delegate.h
+++ b/ios/chrome/browser/push_notification/model/push_notification_delegate.h
@@ -15,7 +15,10 @@
 @interface PushNotificationDelegate
     : NSObject <UNUserNotificationCenterDelegate>
 
-- (instancetype)initWithAppState:(AppState*)appState NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithAppState:(AppState*)appState
+          userNotificationCenter:
+              (UNUserNotificationCenter*)userNotificationCenter
+    NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)init NS_UNAVAILABLE;
 
diff --git a/ios/chrome/browser/push_notification/model/push_notification_delegate.mm b/ios/chrome/browser/push_notification/model/push_notification_delegate.mm
index 9df2ccb..d6c5080 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_delegate.mm
+++ b/ios/chrome/browser/push_notification/model/push_notification_delegate.mm
@@ -541,17 +541,16 @@
   __weak AppState* _appState;
   // Stores blocks to execute once the app is finished foregrounding.
   NSMutableArray<ProceduralBlock>* _runAfterForeground;
-  // Storage for the lazy-loaded `appWideClientManager` property.
-  raw_ptr<PushNotificationClientManager> _appWideClientManager;
 }
 
-- (instancetype)initWithAppState:(AppState*)appState {
+- (instancetype)initWithAppState:(AppState*)appState
+          userNotificationCenter:
+              (UNUserNotificationCenter*)userNotificationCenter {
   if ((self = [super init])) {
     _appState = appState;
     [_appState addObserver:self];
     _metricsRecorder = [[NotificationMetricsRecorder alloc]
-        initWithNotificationCenter:[UNUserNotificationCenter
-                                       currentNotificationCenter]];
+        initWithNotificationCenter:userNotificationCenter];
     _metricsRecorder.classifier = self;
   }
   return self;
@@ -833,12 +832,9 @@
 }
 
 - (PushNotificationClientManager*)appWideClientManager {
-  if (!_appWideClientManager) {
-    _appWideClientManager = GetApplicationContext()
-                                ->GetPushNotificationService()
-                                ->GetPushNotificationClientManager();
-  }
-  return _appWideClientManager;
+  return GetApplicationContext()
+      ->GetPushNotificationService()
+      ->GetPushNotificationClientManager();
 }
 
 #pragma mark - Private
@@ -927,8 +923,10 @@
 
 // Notifies the client manager that the scene is "foreground active".
 - (void)appDidEnterForeground:(SceneState*)sceneState {
-  DCHECK(self.appWideClientManager);
-  self.appWideClientManager->OnSceneActiveForegroundBrowserReady();
+  PushNotificationClientManager* appWideClientManager =
+      self.appWideClientManager;
+  DCHECK(appWideClientManager);
+  appWideClientManager->OnSceneActiveForegroundBrowserReady();
   [self.metricsRecorder
       handleDeliveredNotificationsWithClosure:base::DoNothing()];
 
diff --git a/ios/chrome/browser/push_notification/model/push_notification_delegate_unittest.mm b/ios/chrome/browser/push_notification/model/push_notification_delegate_unittest.mm
new file mode 100644
index 0000000..2fd8cd1c2
--- /dev/null
+++ b/ios/chrome/browser/push_notification/model/push_notification_delegate_unittest.mm
@@ -0,0 +1,190 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/push_notification/model/push_notification_delegate.h"
+
+#import "base/run_loop.h"
+#import "base/test/scoped_run_loop_timeout.h"
+#import "base/test/task_environment.h"
+#import "components/affiliations/core/browser/fake_affiliation_service.h"
+#import "ios/chrome/app/application_delegate/app_state.h"
+#import "ios/chrome/app/application_delegate/app_state_observer.h"
+#import "ios/chrome/app/profile/profile_state.h"
+#import "ios/chrome/browser/affiliations/model/ios_chrome_affiliation_service_factory.h"
+#import "ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_factory.h"
+#import "ios/chrome/browser/push_notification/model/push_notification_profile_service.h"
+#import "ios/chrome/browser/push_notification/model/push_notification_profile_service_factory.h"
+#import "ios/chrome/browser/safety_check/model/ios_chrome_safety_check_manager_factory.h"
+#import "ios/chrome/browser/shared/coordinator/scene/test/fake_scene_state.h"
+#import "ios/chrome/browser/shared/model/browser/browser_list.h"
+#import "ios/chrome/browser/shared/model/browser/browser_list_factory.h"
+#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
+#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
+#import "ios/chrome/browser/shared/model/profile/test/test_profile_manager_ios.h"
+#import "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
+#import "ios/testing/scoped_block_swizzler.h"
+#import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+using base::test::ScopedRunLoopTimeout;
+
+@interface PushNotificationDelegate (Testing) <AppStateObserver>
+@end
+
+// Test fixture for PushNotificationDelegate.
+// class PushNotificationDelegateTest : public TestWithProfile {
+class PushNotificationDelegateTest : public PlatformTest {
+ protected:
+  PushNotificationDelegateTest() {}
+
+  ~PushNotificationDelegateTest() override {}
+
+  void SetUp() override {
+    app_state_ = [[AppState alloc] initWithStartupInformation:nil];
+    [app_state_ startInitialization];
+    profile_ = BuildProfile();
+    scene_state_ = [[FakeSceneState alloc] initWithAppState:app_state_
+                                                    profile:profile_];
+    profile_state_ = [[ProfileState alloc] initWithAppState:app_state_];
+    profile_state_.profile = profile_;
+    [app_state_ profileStateCreated:profile_state_];
+    scene_state_.profileState = profile_state_;
+    [profile_state_ sceneStateConnected:scene_state_];
+
+    CreateUserNotificationCenter();
+    delegate_ = [[PushNotificationDelegate alloc]
+              initWithAppState:app_state_
+        userNotificationCenter:user_notification_center_];
+    [delegate_ appState:app_state_ sceneConnected:scene_state_];
+  }
+
+  void TearDown() override {
+    [scene_state_ shutdown];
+    profile_state_.profile = nullptr;
+  }
+
+  // Returns a mock UNNotification object with the given `identifier`.
+
+  id MockNotification(NSString* identifier) {
+    UNNotificationRequest* request = [UNNotificationRequest
+        requestWithIdentifier:identifier
+                      content:[[UNNotificationContent alloc] init]
+                      trigger:nil];
+    id mock_notification = OCMClassMock([UNNotification class]);
+    OCMStub([mock_notification request]).andReturn(request);
+    return mock_notification;
+  }
+
+  // Creates a mock UNUserNotificationCenter that returns an empty array for
+  // getDeliveredNotificationsWithCompletionHandler.
+  void CreateUserNotificationCenter() {
+    user_notification_center_ = OCMClassMock([UNUserNotificationCenter class]);
+    id block = ^(void (^completionHandler)(NSArray<UNNotification*>*)) {
+      completionHandler(@[]);
+      return YES;
+    };
+    OCMStub([user_notification_center_
+        getDeliveredNotificationsWithCompletionHandler:
+            [OCMArg checkWithBlock:block]]);
+
+    // Swizzle `currentNotificationCenter` because there is other code besides
+    // the PushNotificationDelegate that accesses it.
+    UNUserNotificationCenter* (^swizzle_block)() =
+        ^UNUserNotificationCenter*() {
+          return user_notification_center_;
+        };
+    user_notification_center_swizzler_ = std::make_unique<ScopedBlockSwizzler>(
+        [UNUserNotificationCenter class], @selector(currentNotificationCenter),
+        swizzle_block);
+  }
+
+  // Builds a TestProfileIOS with all the required factories.
+  TestProfileIOS* BuildProfile() {
+    TestProfileIOS::Builder builder;
+    builder.AddTestingFactory(
+        IOSChromeAffiliationServiceFactory::GetInstance(),
+        base::BindOnce([](ProfileIOS*) -> std::unique_ptr<KeyedService> {
+          return std::make_unique<affiliations::FakeAffiliationService>();
+        }));
+    builder.AddTestingFactory(
+        IOSChromePasswordCheckManagerFactory::GetInstance(),
+        IOSChromePasswordCheckManagerFactory::GetDefaultFactory());
+    builder.AddTestingFactory(
+        IOSChromeSafetyCheckManagerFactory::GetInstance(),
+        IOSChromeSafetyCheckManagerFactory::GetDefaultFactory());
+    builder.AddTestingFactory(
+        PushNotificationProfileServiceFactory::GetInstance(),
+        PushNotificationProfileServiceFactory::GetDefaultFactory());
+    return profile_manager_.AddProfileWithBuilder(std::move(builder));
+  }
+
+  // Creates a TestBrowser object and adds it to the Profile's BrowserList.
+  void CreateBrowser() {
+    browser_ = std::make_unique<TestBrowser>(profile_, scene_state_);
+    BrowserList* browser_list = BrowserListFactory::GetForProfile(profile_);
+    browser_list->AddBrowser(browser_.get());
+  }
+
+  // Advances the AppState's initStage to `kFinal`.
+  void SimulateAppStateFinal() {
+    while (app_state_.initStage != AppInitStage::kFinal) {
+      [app_state_ queueTransitionToNextInitStage];
+    }
+    EXPECT_EQ(app_state_.initStage, AppInitStage::kFinal);
+  }
+
+  // Advances the ProfileState's initStage to `kFinal`.
+  void SimulateProfileStateFinal() {
+    while (profile_state_.initStage != ProfileInitStage::kFinal) {
+      [profile_state_ queueTransitionToNextInitStage];
+    }
+    EXPECT_EQ(profile_state_.initStage, ProfileInitStage::kFinal);
+  }
+
+  base::test::TaskEnvironment task_environment_;
+  IOSChromeScopedTestingLocalState scoped_testing_local_state_;
+  TestProfileManagerIOS profile_manager_;
+  raw_ptr<TestProfileIOS> profile_;
+  std::unique_ptr<TestBrowser> browser_;
+  AppState* app_state_;
+  FakeSceneState* scene_state_;
+  ProfileState* profile_state_;
+  UNUserNotificationCenter* user_notification_center_;
+  std::unique_ptr<ScopedBlockSwizzler> user_notification_center_swizzler_;
+  PushNotificationDelegate* delegate_;
+};
+
+// Tests that willPresentNotification runs after the app is foreground active
+// in order to avoid crashing because something is not fully loaded yet.
+TEST_F(PushNotificationDelegateTest, WillPresentNotification) {
+  ScopedRunLoopTimeout scoped_timeout(FROM_HERE, base::Seconds(5));
+  __block bool completion_handler_called = false;
+  base::RunLoop run_loop;
+  base::RepeatingClosure quit = run_loop.QuitClosure();
+  UNNotification* notification = MockNotification(@"identifier");
+
+  // Call willPresentNotification: before the app is fully initialized.
+  // The completion handler should be queued.
+  [delegate_
+       userNotificationCenter:user_notification_center_
+      willPresentNotification:notification
+        withCompletionHandler:^(UNNotificationPresentationOptions options) {
+          completion_handler_called = true;
+          std::move(quit).Run();
+        }];
+
+  // The handler should not have been called yet.
+  EXPECT_FALSE(completion_handler_called);
+
+  // Simulate the AppState and ProfileState going to final, and SceneState
+  // foreground active.
+  CreateBrowser();
+  SimulateAppStateFinal();
+  scene_state_.activationLevel = SceneActivationLevelForegroundActive;
+  SimulateProfileStateFinal();
+
+  // Wait for the completion block to run.
+  run_loop.Run();
+  EXPECT_TRUE(completion_handler_called);
+}
diff --git a/ios/chrome/browser/push_notification/model/push_notification_profile_service_factory.h b/ios/chrome/browser/push_notification/model/push_notification_profile_service_factory.h
index 2f0f2655..8da68b37 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_profile_service_factory.h
+++ b/ios/chrome/browser/push_notification/model/push_notification_profile_service_factory.h
@@ -19,6 +19,11 @@
   static PushNotificationProfileServiceFactory* GetInstance();
   static PushNotificationProfileService* GetForProfile(ProfileIOS* profile);
 
+  // Returns the default factory used to build PushNotificationProfileService.
+  // Can be registered with AddTestingFactory to use real instances during
+  // testing.
+  static TestingFactory GetDefaultFactory();
+
  private:
   friend class base::NoDestructor<PushNotificationProfileServiceFactory>;
 
diff --git a/ios/chrome/browser/push_notification/model/push_notification_profile_service_factory.mm b/ios/chrome/browser/push_notification/model/push_notification_profile_service_factory.mm
index b081806..a030d3f9 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_profile_service_factory.mm
+++ b/ios/chrome/browser/push_notification/model/push_notification_profile_service_factory.mm
@@ -15,6 +15,24 @@
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/signin/model/identity_manager_factory.h"
 
+namespace {
+// Default factory for PushNotificationProfileService.
+std::unique_ptr<KeyedService> BuildInstance(ProfileIOS* profile) {
+  const scoped_refptr<base::SequencedTaskRunner> task_runner =
+      base::SequencedTaskRunner::GetCurrentDefault();
+
+  std::unique_ptr<PushNotificationClientManager> client_manager =
+      IsMultiProfilePushNotificationHandlingEnabled()
+          ? std::make_unique<PushNotificationClientManager>(task_runner,
+                                                            profile)
+          : nullptr;
+
+  return std::make_unique<PushNotificationProfileService>(
+      IdentityManagerFactory::GetForProfile(profile), std::move(client_manager),
+      profile->GetStatePath());
+}
+}  // namespace
+
 // static
 PushNotificationProfileServiceFactory*
 PushNotificationProfileServiceFactory::GetInstance() {
@@ -42,16 +60,11 @@
 std::unique_ptr<KeyedService>
 PushNotificationProfileServiceFactory::BuildServiceInstanceFor(
     ProfileIOS* profile) const {
-  const scoped_refptr<base::SequencedTaskRunner> task_runner =
-      base::SequencedTaskRunner::GetCurrentDefault();
+  return BuildInstance(profile);
+}
 
-  std::unique_ptr<PushNotificationClientManager> client_manager =
-      IsMultiProfilePushNotificationHandlingEnabled()
-          ? std::make_unique<PushNotificationClientManager>(task_runner,
-                                                            profile)
-          : nullptr;
-
-  return std::make_unique<PushNotificationProfileService>(
-      IdentityManagerFactory::GetForProfile(profile), std::move(client_manager),
-      profile->GetStatePath());
+// static
+PushNotificationProfileServiceFactory::TestingFactory
+PushNotificationProfileServiceFactory::GetDefaultFactory() {
+  return base::BindOnce(&BuildInstance);
 }
diff --git a/ios/chrome/browser/push_notification/model/push_notification_settings_util_unittest.mm b/ios/chrome/browser/push_notification/model/push_notification_settings_util_unittest.mm
index 01f6265..66951d8 100644
--- a/ios/chrome/browser/push_notification/model/push_notification_settings_util_unittest.mm
+++ b/ios/chrome/browser/push_notification/model/push_notification_settings_util_unittest.mm
@@ -46,9 +46,8 @@
         initWithProfileManager:&profile_manager_];
     fake_id_ = [FakeSystemIdentity fakeIdentity1];
     // TODO(b/318863934): Remove flag when enabled by default.
-    feature_list_.InitWithFeatures(
-        {/*enabled=*/kContentNotificationExperiment, kSafetyCheckNotifications},
-        {/*disabled=*/});
+    feature_list_.InitWithFeatures({/*enabled=*/kSafetyCheckNotifications},
+                                   {/*disabled=*/});
     AddTestCasesToManager(manager_, profile_attributes_storage(),
                           fake_id_.gaiaId, profile_name);
   }
diff --git a/ios/chrome/browser/reading_list/ui_bundled/reading_list_egtest.mm b/ios/chrome/browser/reading_list/ui_bundled/reading_list_egtest.mm
index 2d64e6d0..3675e889 100644
--- a/ios/chrome/browser/reading_list/ui_bundled/reading_list_egtest.mm
+++ b/ios/chrome/browser/reading_list/ui_bundled/reading_list_egtest.mm
@@ -820,9 +820,7 @@
 
 // Tests that the "Cancel", "Edit" and "Mark Unread" buttons are not visible
 // after delete (using swipe).
-// TODO(crbug.com/440119404): Re-enable after roll is complete and testers are
-// also updated.
-- (void)DISABLED_testVisibleButtonsAfterSwipeDeletion {
+- (void)testVisibleButtonsAfterSwipeDeletion {
   AddEntriesAndOpenReadingList();
 
   [[[EarlGrey selectElementWithMatcher:VisibleReadingListItem(kReadTitle)]
@@ -848,7 +846,7 @@
       waitWithTimeout:base::test::ios::kWaitForUIElementTimeout.InSecondsF()];
 
   if (!matchedElement) {
-    // Delete button is still on screen, tap it
+    // Delete button is still on screen, tap it.
     [[EarlGrey selectElementWithMatcher:deleteButtonMatcher]
         performAction:grey_tap()];
   }
diff --git a/ios/chrome/browser/sad_tab/ui_bundled/sad_tab_view_controller.mm b/ios/chrome/browser/sad_tab/ui_bundled/sad_tab_view_controller.mm
index a9deccd..c426d48 100644
--- a/ios/chrome/browser/sad_tab/ui_bundled/sad_tab_view_controller.mm
+++ b/ios/chrome/browser/sad_tab/ui_bundled/sad_tab_view_controller.mm
@@ -71,10 +71,12 @@
   [self.overscrollActionsController setStyle:style];
   [self updateOverscrollActionsState];
 
-  NSArray<UITrait>* traits = TraitCollectionSetForTraits(
-      @[ UITraitHorizontalSizeClass.class, UITraitVerticalSizeClass.class ]);
-  [self registerForTraitChanges:traits
-                     withAction:@selector(updateOverscrollActionsState)];
+  if (@available(iOS 17, *)) {
+    NSArray<UITrait>* traits = TraitCollectionSetForTraits(
+        @[ UITraitHorizontalSizeClass.class, UITraitVerticalSizeClass.class ]);
+    [self registerForTraitChanges:traits
+                       withAction:@selector(updateOverscrollActionsState)];
+  }
 }
 
 - (void)viewDidLayoutSubviews {
@@ -88,6 +90,17 @@
   [self.scrollView setContentSize:newFrame.size];
 }
 
+#if !defined(__IPHONE_17_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_17_0
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  if (@available(iOS 17, *)) {
+    return;
+  }
+
+  [self updateOverscrollActionsState];
+}
+#endif
+
 #pragma mark - SadTabViewDelegate
 
 - (void)sadTabViewShowReportAnIssue:(SadTabView*)sadTabView {
diff --git a/ios/chrome/browser/save_to_photos/ui_bundled/BUILD.gn b/ios/chrome/browser/save_to_photos/ui_bundled/BUILD.gn
index ff4976de7..bc60a06 100644
--- a/ios/chrome/browser/save_to_photos/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/save_to_photos/ui_bundled/BUILD.gn
@@ -58,6 +58,7 @@
     "//components/prefs:test_support",
     "//components/signin/public/identity_manager:test_support",
     "//components/strings:components_strings_grit",
+    "//components/test/ios",
     "//components/variations:test_support",
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser/account_picker/ui_bundled",
diff --git a/ios/chrome/browser/save_to_photos/ui_bundled/save_to_photos_coordinator_unittest.mm b/ios/chrome/browser/save_to_photos/ui_bundled/save_to_photos_coordinator_unittest.mm
index ec50e2a..e0052e6 100644
--- a/ios/chrome/browser/save_to_photos/ui_bundled/save_to_photos_coordinator_unittest.mm
+++ b/ios/chrome/browser/save_to_photos/ui_bundled/save_to_photos_coordinator_unittest.mm
@@ -8,6 +8,7 @@
 
 #import "base/apple/foundation_util.h"
 #import "components/signin/public/identity_manager/identity_test_environment.h"
+#import "components/test/ios/test_utils.h"
 #import "ios/chrome/browser/account_picker/ui_bundled/account_picker_configuration.h"
 #import "ios/chrome/browser/account_picker/ui_bundled/account_picker_coordinator.h"
 #import "ios/chrome/browser/account_picker/ui_bundled/account_picker_coordinator_delegate.h"
@@ -108,16 +109,12 @@
     OCMStub([mock_save_to_photos_mediator_ alloc])
         .andReturn(mock_save_to_photos_mediator_);
     OCMStub([mock_save_to_photos_mediator_
-                    initWithPhotosService:reinterpret_cast<PhotosService*>(
-                                              [OCMArg anyPointer])
-                              prefService:reinterpret_cast<PrefService*>(
-                                              [OCMArg anyPointer])
-                    accountManagerService:reinterpret_cast<
-                                              ChromeAccountManagerService*>(
-                                              [OCMArg anyPointer])
-                          identityManager:reinterpret_cast<
-                                              signin::IdentityManager*>(
-                                              [OCMArg anyPointer])
+                    initWithPhotosService:ios::OCM::AnyPointer<PhotosService>()
+                              prefService:ios::OCM::AnyPointer<PrefService>()
+                    accountManagerService:ios::OCM::AnyPointer<
+                                              ChromeAccountManagerService>()
+                          identityManager:ios::OCM::AnyPointer<
+                                              signin::IdentityManager>()
                 manageStorageAlertHandler:[OCMArg any]
                        applicationHandler:[OCMArg any]
                          googleOneHandler:[OCMArg any]])
diff --git a/ios/chrome/browser/saved_tab_groups/model/ios_tab_group_sync_delegate_unittest.mm b/ios/chrome/browser/saved_tab_groups/model/ios_tab_group_sync_delegate_unittest.mm
index 4ec5460..3bad345 100644
--- a/ios/chrome/browser/saved_tab_groups/model/ios_tab_group_sync_delegate_unittest.mm
+++ b/ios/chrome/browser/saved_tab_groups/model/ios_tab_group_sync_delegate_unittest.mm
@@ -633,12 +633,12 @@
   OCMStub([mock_application_handler_
       displayTabGridInMode:TabGridOpeningMode::kRegular]);
 
-  OCMStub([mock_tab_groups_handler_
-              showTabGroup:(const TabGroup*)[OCMArg anyPointer]])
+  OCMStub(
+      [mock_tab_groups_handler_ showTabGroup:ios::OCM::AnyPointer<TabGroup>()])
       .andAssignStructParameterToVariable(tab_group_shown, 0);
 
   OCMStub([mock_tab_grid_handler_
-              bringGroupIntoView:(const TabGroup*)[OCMArg anyPointer]
+              bringGroupIntoView:ios::OCM::AnyPointer<TabGroup>()
                         animated:NO])
       .andAssignStructParameterToVariable(tab_group_for_grid, 0);
   EXPECT_CALL(*mock_service_, GetGroup(saved_tab_group_id))
diff --git a/ios/chrome/browser/scanner/ui_bundled/camera_controller.mm b/ios/chrome/browser/scanner/ui_bundled/camera_controller.mm
index 33a0c06..c6dddf4 100644
--- a/ios/chrome/browser/scanner/ui_bundled/camera_controller.mm
+++ b/ios/chrome/browser/scanner/ui_bundled/camera_controller.mm
@@ -117,14 +117,24 @@
 - (void)resetVideoOrientation:(AVCaptureVideoPreviewLayer*)previewLayer {
   DCHECK(previewLayer);
   AVCaptureConnection* videoConnection = [previewLayer connection];
-  AVCaptureDevice* camera = [self camera];
-  AVCaptureDeviceRotationCoordinator* rotationCoordiantor =
-      [[AVCaptureDeviceRotationCoordinator alloc] initWithDevice:camera
-                                                    previewLayer:previewLayer];
-  CGFloat angle = rotationCoordiantor.videoRotationAngleForHorizonLevelCapture;
-  if ([videoConnection isVideoRotationAngleSupported:angle]) {
-    [videoConnection setVideoRotationAngle:angle];
+  if (@available(iOS 17, *)) {
+    AVCaptureDevice* camera = [self camera];
+    AVCaptureDeviceRotationCoordinator* rotationCoordiantor =
+        [[AVCaptureDeviceRotationCoordinator alloc]
+            initWithDevice:camera
+              previewLayer:previewLayer];
+    CGFloat angle =
+        rotationCoordiantor.videoRotationAngleForHorizonLevelCapture;
+    if ([videoConnection isVideoRotationAngleSupported:angle]) {
+      [videoConnection setVideoRotationAngle:angle];
+    }
   }
+#if !defined(__IPHONE_17_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_17_0
+  else if ([videoConnection isVideoOrientationSupported]) {
+    [videoConnection setVideoOrientation:
+                         [self videoOrientationForCurrentInterfaceOrientation]];
+  }
+#endif
 }
 
 - (void)startRecording {
diff --git a/ios/chrome/browser/scanner/ui_bundled/scanner_view.mm b/ios/chrome/browser/scanner/ui_bundled/scanner_view.mm
index cc0a7428..77e39b9 100644
--- a/ios/chrome/browser/scanner/ui_bundled/scanner_view.mm
+++ b/ios/chrome/browser/scanner/ui_bundled/scanner_view.mm
@@ -67,9 +67,12 @@
   }
   DCHECK(delegate);
   _delegate = delegate;
-  NSArray<UITrait>* traits =
-      TraitCollectionSetForTraits(@[ UITraitVerticalSizeClass.class ]);
-  [self registerForTraitChanges:traits withAction:@selector(maybeHideCaptions)];
+  if (@available(iOS 17, *)) {
+    NSArray<UITrait>* traits =
+        TraitCollectionSetForTraits(@[ UITraitVerticalSizeClass.class ]);
+    [self registerForTraitChanges:traits
+                       withAction:@selector(maybeHideCaptions)];
+  }
 
   return self;
 }
@@ -186,6 +189,17 @@
   return @"";
 }
 
+#if !defined(__IPHONE_17_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_17_0
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  if (@available(iOS 17, *)) {
+    return;
+  }
+
+  [self maybeHideCaptions];
+}
+#endif
+
 #pragma mark - private methods
 
 // Creates an image with template rendering mode for use in icons.
diff --git a/ios/chrome/browser/sessions/OWNERS b/ios/chrome/browser/sessions/OWNERS
new file mode 100644
index 0000000..fcd605b
--- /dev/null
+++ b/ios/chrome/browser/sessions/OWNERS
@@ -0,0 +1,2 @@
+fedegermi@google.com
+sdefresne@chromium.org
diff --git a/ios/chrome/browser/sessions/model/OWNERS b/ios/chrome/browser/sessions/model/OWNERS
deleted file mode 100644
index 40a68c7..0000000
--- a/ios/chrome/browser/sessions/model/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-rohitrao@chromium.org
diff --git a/ios/chrome/browser/settings/ui_bundled/DEPS b/ios/chrome/browser/settings/ui_bundled/DEPS
index 36fd067..3810dc3 100644
--- a/ios/chrome/browser/settings/ui_bundled/DEPS
+++ b/ios/chrome/browser/settings/ui_bundled/DEPS
@@ -54,7 +54,7 @@
   "+ios/chrome/browser/voice/model",
   "+ios/chrome/browser/web/model/annotations/annotations_util.h",
   "+ios/chrome/browser/web/model/features.h",
-  "+ios/chrome/browser/webauthn/model",
+  "+ios/chrome/browser/webauthn",
 ]
 
 specific_include_rules = {
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_manager_egtest.mm b/ios/chrome/browser/settings/ui_bundled/password/password_manager_egtest.mm
index ed26e4b..49672ff1 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/password_manager_egtest.mm
+++ b/ios/chrome/browser/settings/ui_bundled/password/password_manager_egtest.mm
@@ -4139,9 +4139,7 @@
       assertWithMatcher:grey_sufficientlyVisible()];
 }
 
-// TODO(crbug.com/440119404): Re-enable after roll is complete and testers are
-// also updated.
-- (void)DISABLED_testSwipingAnotherAffiliatedGroupWhenAnotherIsInEditMode {
+- (void)testSwipingAnotherAffiliatedGroupWhenAnotherIsInEditMode {
   // Form an affiliated group with two passwords.
   SavePasswordFormToProfileStore(/*password=*/@"password1",
                                  /*username=*/@"user1",
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_settings/BUILD.gn b/ios/chrome/browser/settings/ui_bundled/password/password_settings/BUILD.gn
index a07bd5b..925d16c 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/password_settings/BUILD.gn
+++ b/ios/chrome/browser/settings/ui_bundled/password/password_settings/BUILD.gn
@@ -53,6 +53,7 @@
     "//ios/chrome/browser/signin/model:trusted_vault",
     "//ios/chrome/browser/signin/model:trusted_vault_factory",
     "//ios/chrome/browser/sync/model",
+    "//ios/chrome/browser/webauthn/coordinator:export_coordinator",
     "//ios/chrome/browser/webauthn/model",
     "//ios/chrome/common/ui/elements:branded_navigation_item_title_view",
     "//ios/chrome/common/ui/reauthentication",
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_coordinator.mm b/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_coordinator.mm
index 3005ec15..af0dde1 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_coordinator.mm
+++ b/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_coordinator.mm
@@ -53,6 +53,7 @@
 #import "ios/chrome/browser/signin/model/trusted_vault_client_backend.h"
 #import "ios/chrome/browser/signin/model/trusted_vault_client_backend_factory.h"
 #import "ios/chrome/browser/sync/model/sync_service_factory.h"
+#import "ios/chrome/browser/webauthn/coordinator/credential_export_coordinator.h"
 #import "ios/chrome/browser/webauthn/model/ios_passkey_model_factory.h"
 #import "ios/chrome/common/ui/elements/branded_navigation_item_title_view.h"
 #import "ios/chrome/common/ui/reauthentication/reauthentication_module.h"
@@ -180,6 +181,9 @@
 
   // Coordinator for displaying errors in update GPM PIN flow.
   AlertCoordinator* _updateGPMPinErrorCoordinator;
+
+  // Coordinator for handling the credential export flow.
+  CredentialExportCoordinator* _credentialExportCoordinator;
 }
 
 #pragma mark - ChromeCoordinator
@@ -359,6 +363,17 @@
 }
 
 - (void)startExportFlow {
+  if (@available(iOS 26, *)) {
+    if (CredentialExchangeEnabled()) {
+      _credentialExportCoordinator = [[CredentialExportCoordinator alloc]
+          initWithBaseNavigationController:_settingsNavigationController
+                                   browser:self.browser
+                   savedPasswordsPresenter:_savedPasswordsPresenter.get()];
+      [_credentialExportCoordinator start];
+      return;
+    }
+  }
+
   UIAlertController* exportConfirmation = [UIAlertController
       alertControllerWithTitle:nil
                        message:l10n_util::GetNSString(
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_mediator.mm b/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_mediator.mm
index 56ca880..b789cda 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_mediator.mm
+++ b/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_mediator.mm
@@ -22,14 +22,12 @@
 #import "components/sync/base/user_selectable_type.h"
 #import "components/sync/service/sync_service_utils.h"
 #import "components/sync/service/sync_user_settings.h"
-#import "ios/chrome/browser/credential_provider/model/features.h"
 #import "ios/chrome/browser/settings/ui_bundled/password/password_exporter.h"
 #import "ios/chrome/browser/settings/ui_bundled/password/saved_passwords_presenter_observer.h"
 #import "ios/chrome/browser/settings/ui_bundled/utils/password_auto_fill_status_manager.h"
 #import "ios/chrome/browser/signin/model/system_identity.h"
 #import "ios/chrome/browser/signin/model/trusted_vault_client_backend.h"
 #import "ios/chrome/browser/sync/model/sync_observer_bridge.h"
-#import "ios/chrome/browser/webauthn/model/credential_exporter.h"
 #import "ios/chrome/common/ui/reauthentication/reauthentication_protocol.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ui/base/l10n/l10n_util.h"
@@ -231,22 +229,9 @@
 }
 
 - (void)userDidStartExportFlow:(UIWindow*)window {
-  BOOL didRunCredentialExchange = NO;
-
-  if (@available(iOS 26, *)) {
-    if (CredentialExchangeEnabled()) {
-      CredentialExporter* credentialExporter =
-          [[CredentialExporter alloc] initWithWindow:window
-                             savedPasswordsPresenter:_savedPasswordsPresenter];
-      [credentialExporter startExport];
-      didRunCredentialExchange = YES;
-    }
-  }
-  if (!didRunCredentialExchange) {
-    std::vector<CredentialUIEntry> passwords =
-        _savedPasswordsPresenter->GetSavedPasswords();
-    [_passwordExporter startExportFlow:passwords];
-  }
+  std::vector<CredentialUIEntry> passwords =
+      _savedPasswordsPresenter->GetSavedPasswords();
+  [_passwordExporter startExportFlow:passwords];
 }
 
 - (void)userDidCompleteExportFlow {
diff --git a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
index 0b0dabfb..38caff0 100644
--- a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
+++ b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
@@ -62,7 +62,7 @@
 #import "ios/chrome/browser/authentication/ui_bundled/change_profile/change_profile_signout_continuation.h"
 #import "ios/chrome/browser/authentication/ui_bundled/continuation.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/features.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_scene_agent.h"
+#import "ios/chrome/browser/authentication/ui_bundled/signin/promo/fullscreen_signin_promo_scene_agent.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_in_progress.h"
@@ -236,14 +236,6 @@
 
 namespace {
 
-// TODO(crbug.com/429351158): Remove
-// kMakeKeyAndVisibleBeforeMainCoordinatorStart feature. Killswitch, can be
-// removed around February 2024. If enabled, createInitialUI will call
-// makeKeyAndVisible before mainCoordinator start. When disabled, this fix
-// resolves a flicker when starting the app in light mode
-BASE_FEATURE(kMakeKeyAndVisibleBeforeMainCoordinatorStart,
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 // Feature to control whether Search Intents (Widgets, Application
 // Shortcuts menu) forcibly open a new tab, rather than reusing an
 // existing NTP. See http://crbug.com/1363375 for details.
@@ -1260,7 +1252,7 @@
   if (level == SceneActivationLevelForegroundActive &&
       profileInitStage == ProfileInitStage::kFinal) {
     if (!IsFullscreenSigninPromoManagerMigrationEnabled()) {
-      [self tryPresentSigninUpgradePromo];
+      [self tryPresentFullscreenSigninPromo];
     }
 
     if ([self handleExternalIntents]) {
@@ -1429,7 +1421,7 @@
   if (IsFullscreenSigninPromoManagerMigrationEnabled()) {
     [sceneState
         addAgent:
-            [[SigninFullscreenPromoSceneAgent alloc]
+            [[FullscreenSigninPromoSceneAgent alloc]
                 initWithPromosManager:promosManager
                           authService:authService
                       identityManager:IdentityManagerFactory::GetForProfile(
@@ -1504,11 +1496,6 @@
       _webStateListForwardingObserver.get());
   _mainWebStateObserver->Observe(self.mainInterface.browser->GetWebStateList());
 
-  if (base::FeatureList::IsEnabled(
-          kMakeKeyAndVisibleBeforeMainCoordinatorStart)) {
-    [self.sceneState.window makeKeyAndVisible];
-  }
-
   _mainCoordinator = [[TabGridCoordinator alloc]
       initWithApplicationCommandEndpoint:self
                           regularBrowser:self.mainInterface.browser
@@ -1518,14 +1505,11 @@
 
   [_mainCoordinator start];
 
-  if (!base::FeatureList::IsEnabled(
-          kMakeKeyAndVisibleBeforeMainCoordinatorStart)) {
-    // Enables UI initializations to query the keyWindow's size. Do this after
-    // `mainCoordinator start` as it sets self.window.rootViewController to work
-    // around crbug.com/850387, causing a flicker if -makeKeyAndVisible has been
-    // called.
-    [self.sceneState.window makeKeyAndVisible];
-  }
+  // Enables UI initializations to query the keyWindow's size. Do this after
+  // `mainCoordinator start` as it sets self.window.rootViewController to work
+  // around crbug.com/850387, causing a flicker if -makeKeyAndVisible has been
+  // called.
+  [self.sceneState.window makeKeyAndVisible];
 
   if (!self.sceneState.profileState.startupInformation.isFirstRun) {
     [self reconcileEulaAsAccepted];
@@ -1747,8 +1731,8 @@
   });
 }
 
-// Returns YES if the sign-in upgrade promo should be presented.
-- (BOOL)shouldPresentSigninUpgradePromo {
+// Returns YES if the fullscreen sign-in promo should be presented.
+- (BOOL)shouldPresentFullscreenSigninPromo {
   if (![self isTabAvailableToPresentViewController]) {
     return NO;
   }
@@ -1769,22 +1753,24 @@
     return NO;
   }
   // Don't show the promo if already presented.
-  if (self.sceneState.profileState.appState.signinUpgradePromoPresentedOnce) {
+  if (self.sceneState.profileState.appState
+          .fullscreenSigninPromoPresentedOnce) {
     return NO;
   }
   return YES;
 }
 
-// Presents the sign-in upgrade promo.
-- (void)tryPresentSigninUpgradePromo {
+// Presents the fullscreen sign-in  promo.
+- (void)tryPresentFullscreenSigninPromo {
   // It is possible during a slow asynchronous call that the user changes their
   // state so as to no longer be eligible for sign-in promos. Return early in
   // this case.
-  if (![self shouldPresentSigninUpgradePromo]) {
+  if (![self shouldPresentFullscreenSigninPromo]) {
     return;
   }
-  self.sceneState.profileState.appState.signinUpgradePromoPresentedOnce = YES;
-  [self showSigninUpgradePromoWithCompletion:nil];
+  self.sceneState.profileState.appState.fullscreenSigninPromoPresentedOnce =
+      YES;
+  [self showFullscreenSigninPromoWithCompletion:nil];
 }
 
 - (BOOL)canHandleIntents {
@@ -1893,7 +1879,7 @@
 
 #pragma mark - ApplicationCommands
 
-- (void)showSigninUpgradePromoWithCompletion:
+- (void)showFullscreenSigninPromoWithCompletion:
     (SigninCoordinatorCompletionCallback)dismissalCompletion {
   DCHECK(!self.signinCoordinator)
       << "self.signinCoordinator: "
@@ -1901,13 +1887,13 @@
   Browser* browser = self.mainInterface.browser;
   [self stopSigninCoordinatorWithCompletionAnimated:NO];
   self.signinCoordinator = [SigninCoordinator
-      upgradeSigninPromoCoordinatorWithBaseViewController:self.mainInterface
-                                                              .viewController
-                                                  browser:browser
-                                             contextStyle:SigninContextStyle::
-                                                              kDefault
-                        changeProfileContinuationProvider:
-                            DoNothingContinuationProvider()];
+      fullscreenSigninPromoCoordinatorWithBaseViewController:self.mainInterface
+                                                                 .viewController
+                                                     browser:browser
+                                                contextStyle:
+                                                    SigninContextStyle::kDefault
+                           changeProfileContinuationProvider:
+                               DoNothingContinuationProvider()];
   [self startSigninCoordinatorWithCompletion:dismissalCompletion];
 }
 
diff --git a/ios/chrome/browser/shared/model/prefs/BUILD.gn b/ios/chrome/browser/shared/model/prefs/BUILD.gn
index c2a67832..dbc6fa6 100644
--- a/ios/chrome/browser/shared/model/prefs/BUILD.gn
+++ b/ios/chrome/browser/shared/model/prefs/BUILD.gn
@@ -74,6 +74,8 @@
     "//components/reading_list/core",
     "//components/regional_capabilities",
     "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//components/safety_check:pref_names",
+    "//components/safety_check:prefs",
     "//components/saved_tab_groups/public",
     "//components/search_engines",
     "//components/segmentation_platform/embedder/default_model",
@@ -174,6 +176,7 @@
     "//components/policy/core/common:common_constants",
     "//components/prefs",
     "//components/prefs:test_support",
+    "//components/safety_check:pref_names",
     "//components/signin/public/identity_manager",
     "//components/sync_preferences:test_support",
     "//ios/chrome/browser/content_suggestions/ui_bundled/safety_check:prefs",
diff --git a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
index 63abd73..d16de1f 100644
--- a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
@@ -61,6 +61,8 @@
 #import "components/proxy_config/pref_proxy_config_tracker_impl.h"
 #import "components/regional_capabilities/regional_capabilities_prefs.h"
 #import "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#import "components/safety_check/safety_check_pref_names.h"
+#import "components/safety_check/safety_check_prefs.h"
 #import "components/saved_tab_groups/public/pref_names.h"
 #import "components/search_engines/template_url_prepopulate_data.h"
 #import "components/segmentation_platform/embedder/default_model/device_switcher_result_dispatcher.h"
@@ -958,8 +960,6 @@
   registry->RegisterBooleanPref(prefs::kHomeCustomizationMagicStackTipsEnabled,
                                 true);
   registry->RegisterBooleanPref(
-      prefs::kHomeCustomizationMagicStackSafetyCheckEnabled, true);
-  registry->RegisterBooleanPref(
       prefs::kHomeCustomizationMagicStackTabResumptionEnabled, true);
 
   safety_check_prefs::RegisterPrefs(registry);
@@ -1007,6 +1007,8 @@
       prefs::kIosSafetyCheckManagerInsecurePasswordCounts,
       PrefRegistry::LOSSY_PREF);
 
+  safety_check::prefs::RegisterProfilePrefs(registry);
+
   // Preferences related to Lens Overlay.
   registry->RegisterBooleanPref(prefs::kLensOverlayConditionsAccepted, false);
 
@@ -1106,6 +1108,11 @@
   registry->RegisterBooleanPref(kTipsInMagicStackDisabledPref, false);
   registry->RegisterBooleanPref(kHomeCustomizationMagicStackSetUpListEnabled,
                                 true);
+
+  // Deprecated 10/2025. Use
+  // `safety_check::prefs::kSafetyCheckHomeModuleEnabled` instead.
+  registry->RegisterBooleanPref(
+      prefs::kHomeCustomizationMagicStackSafetyCheckEnabled, true);
 }
 
 // This method should be periodically pruned of year+ old migrations.
@@ -1283,6 +1290,11 @@
   // Added 10/2025
   prefs->ClearPref(kTipsInMagicStackDisabledPref);
   prefs->ClearPref(kHomeCustomizationMagicStackSetUpListEnabled);
+
+  // Added 10/2025.
+  RenameBooleanPref(safety_check::prefs::kSafetyCheckHomeModuleEnabled,
+                    prefs::kHomeCustomizationMagicStackSafetyCheckEnabled,
+                    prefs);
 }
 
 void MigrateObsoleteUserDefault() {
diff --git a/ios/chrome/browser/shared/model/prefs/browser_prefs_unittest.mm b/ios/chrome/browser/shared/model/prefs/browser_prefs_unittest.mm
index d632b4c9..f18585f 100644
--- a/ios/chrome/browser/shared/model/prefs/browser_prefs_unittest.mm
+++ b/ios/chrome/browser/shared/model/prefs/browser_prefs_unittest.mm
@@ -7,6 +7,7 @@
 #import "components/omnibox/browser/omnibox_pref_names.h"
 #import "components/password_manager/core/common/password_manager_pref_names.h"
 #import "components/policy/core/common/policy_pref_names.h"
+#import "components/safety_check/safety_check_pref_names.h"
 #import "components/signin/public/identity_manager/identity_manager.h"
 #import "components/sync_preferences/testing_pref_service_syncable.h"
 #import "ios/chrome/browser/content_suggestions/ui_bundled/safety_check/safety_check_prefs.h"
@@ -69,6 +70,11 @@
   local_state()->SetInteger(prefs::kNTPHomeCustomizationNewBadgeImpressionCount,
                             99);
 
+  // Set the old Safety Check module pref value to test its migration to the new
+  // name.
+  pref_service_.SetBoolean(
+      prefs::kHomeCustomizationMagicStackSafetyCheckEnabled, false);
+
   // Bottom omnibox position
   local_state()->SetBoolean(prefs::kBottomOmnibox, true);
 
@@ -129,6 +135,13 @@
                 prefs::kNTPHomeCustomizationNewBadgeImpressionCount),
             99);
 
+  EXPECT_FALSE(pref_service_.GetBoolean(
+      prefs::kHomeCustomizationMagicStackSafetyCheckEnabled));
+  EXPECT_TRUE(
+      pref_service_
+          .FindPreference(safety_check::prefs::kSafetyCheckHomeModuleEnabled)
+          ->IsDefaultValue());
+
   // Check bottom omnibox position.
   EXPECT_TRUE(local_state()->GetBoolean(prefs::kBottomOmnibox));
   EXPECT_TRUE(local_state()
@@ -196,6 +209,15 @@
                 prefs::kNTPHomeCustomizationNewBadgeImpressionCount),
             0);
 
+  EXPECT_TRUE(
+      pref_service_
+          .FindPreference(prefs::kHomeCustomizationMagicStackSafetyCheckEnabled)
+          ->IsDefaultValue());
+  // The new pref `safety_check::prefs::kSafetyCheckHomeModuleEnabled` should
+  // now be false (the migrated value).
+  EXPECT_FALSE(pref_service_.GetBoolean(
+      safety_check::prefs::kSafetyCheckHomeModuleEnabled));
+
   // Check bottom omnibox position.
   EXPECT_TRUE(
       local_state()->FindPreference(prefs::kBottomOmnibox)->IsDefaultValue());
diff --git a/ios/chrome/browser/shared/public/commands/BUILD.gn b/ios/chrome/browser/shared/public/commands/BUILD.gn
index 374bd00..97c7f51 100644
--- a/ios/chrome/browser/shared/public/commands/BUILD.gn
+++ b/ios/chrome/browser/shared/public/commands/BUILD.gn
@@ -33,6 +33,7 @@
     "drive_file_picker_commands.h",
     "enhanced_calendar_commands.h",
     "feed_commands.h",
+    "file_upload_panel_commands.h",
     "find_in_page_commands.h",
     "generate_qr_code_command.h",
     "generate_qr_code_command.mm",
diff --git a/ios/chrome/browser/shared/public/commands/application_commands.h b/ios/chrome/browser/shared/public/commands/application_commands.h
index 2690b2d..3f0443a 100644
--- a/ios/chrome/browser/shared/public/commands/application_commands.h
+++ b/ios/chrome/browser/shared/public/commands/application_commands.h
@@ -166,9 +166,9 @@
 // Opens a debug menu for AI prototyping.
 - (void)openAIMenu;
 
-// Shows the sign-in upgrade promo with a completion block that is called when
-// the promo is dismissed.
-- (void)showSigninUpgradePromoWithCompletion:
+// Shows the fullscreen sign-in promo with a completion block that is called
+// when the promo is dismissed.
+- (void)showFullscreenSigninPromoWithCompletion:
     (SigninCoordinatorCompletionCallback)dismissalCompletion;
 
 // Shows the user the modal that contains a button to start the workflow to
diff --git a/ios/chrome/browser/shared/public/commands/file_upload_panel_commands.h b/ios/chrome/browser/shared/public/commands/file_upload_panel_commands.h
new file mode 100644
index 0000000..52591ed
--- /dev/null
+++ b/ios/chrome/browser/shared/public/commands/file_upload_panel_commands.h
@@ -0,0 +1,21 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_FILE_UPLOAD_PANEL_COMMANDS_H_
+#define IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_FILE_UPLOAD_PANEL_COMMANDS_H_
+
+#import <Foundation/Foundation.h>
+
+// Commands to show/hide the file upload panel.
+@protocol FileUploadPanelCommands <NSObject>
+
+// Shows the file upload panel for the active tab.
+- (void)showFileUploadPanel API_AVAILABLE(ios(18.4));
+
+// Hides the file upload panel.
+- (void)hideFileUploadPanel API_AVAILABLE(ios(18.4));
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_FILE_UPLOAD_PANEL_COMMANDS_H_
diff --git a/ios/chrome/browser/shared/public/commands/promos_manager_commands.h b/ios/chrome/browser/shared/public/commands/promos_manager_commands.h
index 5c3f9f14..2cd455f 100644
--- a/ios/chrome/browser/shared/public/commands/promos_manager_commands.h
+++ b/ios/chrome/browser/shared/public/commands/promos_manager_commands.h
@@ -30,8 +30,8 @@
 // Shows the default browser promo after the user tapped Remind Me Later.
 - (void)showDefaultBrowserPromoAfterRemindMeLater;
 
-// Shows the sign-in fullscreen promo.
-- (void)showSigninPromo;
+// Shows the fullscreen sign-in promo.
+- (void)showFullscreenSigninPromo;
 
 // Shows the Welcome Back promo.
 - (void)showWelcomeBackPromo;
diff --git a/ios/chrome/browser/shared/public/features/features.h b/ios/chrome/browser/shared/public/features/features.h
index 5df009d..9012f4c 100644
--- a/ios/chrome/browser/shared/public/features/features.h
+++ b/ios/chrome/browser/shared/public/features/features.h
@@ -303,6 +303,9 @@
 // Used to gate the immersive SRP in the AIM prototype.
 BASE_DECLARE_FEATURE(kAIMPrototypeImmersiveSRP);
 
+// Feature flag for the tab picker in the aim prototype.
+BASE_DECLARE_FEATURE(kAIMPrototypeTabPicker);
+
 // Variations of AIM prototype.
 extern const char kAIMPrototypeParam[];
 extern const char kAIMPrototypeParamAllOmniboxEntrypoints[];
@@ -491,8 +494,6 @@
 // Feature flag to enable the content notifications.
 BASE_DECLARE_FEATURE(kContentPushNotifications);
 
-// Feature flag to enable Content Notification experiments.
-BASE_DECLARE_FEATURE(kContentNotificationExperiment);
 
 // Feature flag to enable Content Notification Provisional without any
 // conditions.
@@ -623,9 +624,6 @@
 // YES when Follow UI Update is enabled.
 bool IsFollowUIUpdateEnabled();
 
-// YES if content push notification experiments are enabled.
-bool IsContentNotificationExperimentEnabled();
-
 // YES when any of the content push notification variations are enabled.
 bool IsContentPushNotificationsEnabled();
 
@@ -1075,4 +1073,26 @@
 BASE_DECLARE_FEATURE(kSynchronousEditMenuItems);
 bool ShouldShowEditMenuItemsSynchronously();
 
+// Feature flag for tips notifications alternative string experiment.
+BASE_DECLARE_FEATURE(kIOSTipsNotificationsAlternativeStrings);
+bool IsTipsNotificationsAlternativeStringsEnabled();
+
+// Name of the parameter that controls tips notifications alternative string
+// version.
+extern const char kTipsNotificationsAlternativeStringVersion[];
+
+// Tips notifications alternative string version for
+// ```kIOSTipsNotificationsAlternativeStrings``` experiment.
+enum class TipsNotificationsAlternativeStringVersion {
+  kDefault = 0,
+  kAlternative1 = 1,
+  kAlternative2 = 2,
+  kAlternative3 = 3,
+};
+
+// Returns the string alternative version for
+// ```kIOSTipsNotificationsAlternativeStrings``` experiment.
+TipsNotificationsAlternativeStringVersion
+GetTipsNotificationsAlternativeStringVersion();
+
 #endif  // IOS_CHROME_BROWSER_SHARED_PUBLIC_FEATURES_FEATURES_H_
diff --git a/ios/chrome/browser/shared/public/features/features.mm b/ios/chrome/browser/shared/public/features/features.mm
index dbb79b13..08f434b 100644
--- a/ios/chrome/browser/shared/public/features/features.mm
+++ b/ios/chrome/browser/shared/public/features/features.mm
@@ -220,6 +220,9 @@
 // Used to gate the immersive SRP in the AIM prototype.
 BASE_FEATURE(kAIMPrototypeImmersiveSRP, base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Feature flag for the tab picker in the aim prototype.
+BASE_FEATURE(kAIMPrototypeTabPicker, base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kOmniboxDRSPrototype, base::FEATURE_DISABLED_BY_DEFAULT);
 
 BASE_FEATURE(kEnableTraitCollectionWorkAround,
@@ -403,12 +406,6 @@
 
 BASE_FEATURE(kContentPushNotifications, base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kContentNotificationExperiment, base::FEATURE_ENABLED_BY_DEFAULT);
-
-bool IsContentNotificationExperimentEnabled() {
-  return base::FeatureList::IsEnabled(kContentNotificationExperiment);
-}
-
 BASE_FEATURE(kContentNotificationProvisionalIgnoreConditions,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
@@ -1146,3 +1143,23 @@
   }
   return false;
 }
+
+BASE_FEATURE(kIOSTipsNotificationsAlternativeStrings,
+             base::FEATURE_DISABLED_BY_DEFAULT);
+bool IsTipsNotificationsAlternativeStringsEnabled() {
+  return base::FeatureList::IsEnabled(kIOSTipsNotificationsAlternativeStrings);
+}
+
+const char kTipsNotificationsAlternativeStringVersion[] =
+    "TipsNotificationsAlternativeStringVersion";
+
+TipsNotificationsAlternativeStringVersion
+GetTipsNotificationsAlternativeStringVersion() {
+  return static_cast<TipsNotificationsAlternativeStringVersion>(
+      base::GetFieldTrialParamByFeatureAsInt(
+          kIOSTipsNotificationsAlternativeStrings,
+          kTipsNotificationsAlternativeStringVersion,
+          /*default_value=*/
+          static_cast<int>(
+              TipsNotificationsAlternativeStringVersion::kDefault)));
+}
diff --git a/ios/chrome/browser/shared/ui/animated_promo/animated_promo_view_controller.mm b/ios/chrome/browser/shared/ui/animated_promo/animated_promo_view_controller.mm
index 8e76d17..d29043a 100644
--- a/ios/chrome/browser/shared/ui/animated_promo/animated_promo_view_controller.mm
+++ b/ios/chrome/browser/shared/ui/animated_promo/animated_promo_view_controller.mm
@@ -165,7 +165,7 @@
   LottieAnimationConfiguration* config =
       [[LottieAnimationConfiguration alloc] init];
   config.animationName = animationAssetName;
-  config.loopAnimationCount = 1000;
+  config.shouldLoop = YES;
   return ios::provider::GenerateLottieAnimation(config);
 }
 
diff --git a/ios/chrome/browser/signin/model/fake_system_identity.mm b/ios/chrome/browser/signin/model/fake_system_identity.mm
index 1a39cf7..12d96b4 100644
--- a/ios/chrome/browser/signin/model/fake_system_identity.mm
+++ b/ios/chrome/browser/signin/model/fake_system_identity.mm
@@ -26,7 +26,7 @@
   NSData* data = [NSKeyedArchiver archivedDataWithRootObject:identities
                                        requiringSecureCoding:NO
                                                        error:&error];
-  DCHECK(!error);
+  CHECK(!error);
   NSString* string = [data base64EncodedStringWithOptions:
                                NSDataBase64EncodingEndLineWithCarriageReturn];
   return base::SysNSStringToUTF8(string);
@@ -80,10 +80,12 @@
 
 - (instancetype)initWithEmail:(NSString*)email gaiaID:(NSString*)gaiaID {
   if ((self = [super init])) {
+    CHECK(gaiaID);
+    CHECK(gaiaID.length);
     _gaiaID = gaiaID;
     _userEmail = [email copy];
     NSArray* split = [email componentsSeparatedByString:@"@"];
-    DCHECK_EQ(split.count, 2ul);
+    CHECK_EQ(split.count, 2ul);
     _userFullName = split[0];
     _userGivenName = split[0];
     _hasValidAuth = YES;
diff --git a/ios/chrome/browser/signin/model/test_account_info.mm b/ios/chrome/browser/signin/model/test_account_info.mm
index 527b2d0..09f257b 100644
--- a/ios/chrome/browser/signin/model/test_account_info.mm
+++ b/ios/chrome/browser/signin/model/test_account_info.mm
@@ -174,8 +174,8 @@
   NSDictionary<NSString*, NSNumber*>* capabilities =
       [coder decodeObjectOfClasses:capabilityClasses
                             forKey:kCoderUserCapabilityKey];
-  return [self initWithUserEmail:gaiaID
-                          gaiaID:userEmail
+  return [self initWithUserEmail:userEmail
+                          gaiaID:gaiaID
                     userFullName:userFullName
                    userGivenName:userGivenName
                     capabilities:capabilities];
diff --git a/ios/chrome/browser/start_surface/ui_bundled/start_surface_features.h b/ios/chrome/browser/start_surface/ui_bundled/start_surface_features.h
index 794ec597..1fd2dc3a 100644
--- a/ios/chrome/browser/start_surface/ui_bundled/start_surface_features.h
+++ b/ios/chrome/browser/start_surface/ui_bundled/start_surface_features.h
@@ -25,6 +25,9 @@
 // Feature to enable Startup time remediations in the Start Surface.
 BASE_DECLARE_FEATURE(kIOSStartTimeStartupRemediations);
 
+// The feature to enable or disable the TabGrid.
+BASE_DECLARE_FEATURE(kShowTabGridOnStart);
+
 // The feature parameter to indicate inactive duration to return to the Start
 // Surface in seconds.
 extern const char kReturnToStartSurfaceInactiveDurationInSeconds[];
@@ -41,9 +44,16 @@
 // "SaveNewNTPWebState" remediation will be enabled.
 extern const char kIOSStartTimeStartupRemediationsSaveNTPWebState[];
 
+// The feature parameter to indicate inactive duration to return to the TabGrid
+// view in seconds.
+extern const char kShowTabGridInactiveDurationInSeconds[];
+
 // Checks whether the Start Surface should be enabled.
 bool IsStartSurfaceEnabled();
 
+// Checks whether the TabGridOnStart should be enabled.
+bool IsTabGridOnStartEnabled();
+
 // Returns the inactive duration to show the Start Surface.
 base::TimeDelta GetReturnToStartSurfaceDuration();
 
@@ -59,4 +69,7 @@
 // kIOSStartTimeBrowserBackgroundRemediations is enabled.
 bool IsAvoidFeedRefreshOnBackgroundEnabled();
 
+// Returns the inactive duration to show the TabGrid view.
+base::TimeDelta GetReturnToTabGridDuration();
+
 #endif  // IOS_CHROME_BROWSER_START_SURFACE_UI_BUNDLED_START_SURFACE_FEATURES_H_
diff --git a/ios/chrome/browser/start_surface/ui_bundled/start_surface_features.mm b/ios/chrome/browser/start_surface/ui_bundled/start_surface_features.mm
index 40ba7e4..3c9bbff 100644
--- a/ios/chrome/browser/start_surface/ui_bundled/start_surface_features.mm
+++ b/ios/chrome/browser/start_surface/ui_bundled/start_surface_features.mm
@@ -11,9 +11,12 @@
 // Default value for kReturnToStartSurfaceInactiveDurationInSeconds.
 constexpr base::TimeDelta kDefaultReturnToStartSurfaceInactiveDuration =
     base::Hours(4);
-
+// Default value for kDefaultShowTabGridInactiveDurationInSeconds.
+constexpr base::TimeDelta kDefaultShowTabGridInactiveDuration = base::Hours(1);
 }  // anonymous namespace
 
+BASE_FEATURE(kShowTabGridOnStart, base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kStartSurface, base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kIOSStartTimeBrowserBackgroundRemediations,
@@ -22,6 +25,9 @@
 BASE_FEATURE(kIOSStartTimeStartupRemediations,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+const char kShowTabGridInactiveDurationInSeconds[] =
+    "ShowTabGridInactiveDurationInSeconds";
+
 const char kReturnToStartSurfaceInactiveDurationInSeconds[] =
     "ReturnToStartSurfaceInactiveDurationInSeconds";
 
@@ -38,6 +44,16 @@
   return base::FeatureList::IsEnabled(kStartSurface);
 }
 
+bool IsTabGridOnStartEnabled() {
+  return base::FeatureList::IsEnabled(kShowTabGridOnStart);
+}
+
+base::TimeDelta GetReturnToTabGridDuration() {
+  return base::Seconds(base::GetFieldTrialParamByFeatureAsDouble(
+      kShowTabGridOnStart, kShowTabGridInactiveDurationInSeconds,
+      kDefaultShowTabGridInactiveDuration.InSecondsF()));
+}
+
 base::TimeDelta GetReturnToStartSurfaceDuration() {
   return base::Seconds(base::GetFieldTrialParamByFeatureAsDouble(
       kStartSurface, kReturnToStartSurfaceInactiveDurationInSeconds,
diff --git a/ios/chrome/browser/sync/model/prefs/cross_device_pref_tracker/OWNERS b/ios/chrome/browser/sync/model/prefs/cross_device_pref_tracker/OWNERS
new file mode 100644
index 0000000..d766c0f3
--- /dev/null
+++ b/ios/chrome/browser/sync/model/prefs/cross_device_pref_tracker/OWNERS
@@ -0,0 +1,3 @@
+bwwilliams@google.com
+scottyoder@google.com
+treib@google.com
\ No newline at end of file
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_sync_egtest.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_sync_egtest.mm
index 2f9b2ca..9524f3a 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_sync_egtest.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_sync_egtest.mm
@@ -240,9 +240,16 @@
                   @"The number of saved tab groups should be 0.");
 }
 
+// TODO(crbug.com/449704034): Flaky on iphone simulator.
 // Tests that renaming a group in the tab grid reflects the change in the
 // Tab Groups panel.
 - (void)testRenameGroupInTabGrid {
+#if TARGET_OS_SIMULATOR
+  if (![ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_SKIPPED(@"Flaky on iphone");
+  }
+#endif
+
   [ChromeEarlGreyUI openTabGrid];
 
   // Create a tab group with an item at 0.
diff --git a/ios/chrome/browser/text_selection/model/text_classifier_model_service.mm b/ios/chrome/browser/text_selection/model/text_classifier_model_service.mm
index 2a44748..0ec024a 100644
--- a/ios/chrome/browser/text_selection/model/text_classifier_model_service.mm
+++ b/ios/chrome/browser/text_selection/model/text_classifier_model_service.mm
@@ -7,6 +7,7 @@
 #import <string>
 
 #import "base/files/file_path.h"
+#import "base/task/thread_pool.h"
 #import "components/optimization_guide/core/delivery/optimization_guide_model_provider.h"
 #import "components/optimization_guide/core/optimization_guide_logger.h"
 #import "components/optimization_guide/proto/models.pb.h"
@@ -18,7 +19,10 @@
   DCHECK(opt_guide_service_);
   opt_guide_service_->AddObserverForOptimizationTargetModel(
       optimization_guide::proto::OPTIMIZATION_TARGET_TEXT_CLASSIFIER,
-      /*model_metadata=*/std::nullopt, this);
+      /*model_metadata=*/std::nullopt,
+      base::ThreadPool::CreateSequencedTaskRunner(
+          {base::MayBlock(), base::TaskPriority::USER_VISIBLE}),
+      this);
   opt_guide_service_->RegisterOptimizationTypes(
       {optimization_guide::proto::TEXT_CLASSIFIER_ENTITY_DETECTION});
 }
diff --git a/ios/chrome/browser/tips_notifications/ui/tips_promo_view_controller.mm b/ios/chrome/browser/tips_notifications/ui/tips_promo_view_controller.mm
index caec907e9..ae790ee 100644
--- a/ios/chrome/browser/tips_notifications/ui/tips_promo_view_controller.mm
+++ b/ios/chrome/browser/tips_notifications/ui/tips_promo_view_controller.mm
@@ -67,7 +67,7 @@
   LottieAnimationConfiguration* config =
       [[LottieAnimationConfiguration alloc] init];
   config.animationName = animationAssetName;
-  config.loopAnimationCount = -1;  // Always loop.
+  config.shouldLoop = YES;
   id<LottieAnimation> animationWrapper =
       ios::provider::GenerateLottieAnimation(config);
   [animationWrapper setDictionaryTextProvider:self.animationTextProvider];
diff --git a/ios/chrome/browser/web/model/choose_file/BUILD.gn b/ios/chrome/browser/web/model/choose_file/BUILD.gn
index 953cdeed..f11a35b 100644
--- a/ios/chrome/browser/web/model/choose_file/BUILD.gn
+++ b/ios/chrome/browser/web/model/choose_file/BUILD.gn
@@ -23,6 +23,7 @@
     ":choose_file_file_utils",
     ":choose_file_js",
     "//base",
+    "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
     "//ios/web/public",
     "//ios/web/public/js_messaging",
@@ -58,9 +59,11 @@
     "//base",
     "//base/test:test_support",
     "//ios/chrome/browser/shared/model/profile/test",
+    "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
     "//ios/web/public/test",
     "//testing/gtest",
+    "//third_party/ocmock",
   ]
 }
 
diff --git a/ios/chrome/browser/web/model/choose_file/choose_file_event.h b/ios/chrome/browser/web/model/choose_file/choose_file_event.h
index dd2fa87..1c8f7eb 100644
--- a/ios/chrome/browser/web/model/choose_file/choose_file_event.h
+++ b/ios/chrome/browser/web/model/choose_file/choose_file_event.h
@@ -26,6 +26,7 @@
     ~Builder();
 
     Builder& SetAllowMultipleFiles(bool value);
+    Builder& SetOnlyAllowDirectory(bool value);
     Builder& SetHasSelectedFile(bool value);
     Builder& SetAcceptFileExtensions(std::vector<std::string> value);
     Builder& SetAcceptMimeTypes(std::vector<std::string> value);
@@ -36,6 +37,7 @@
 
    private:
     bool allow_multiple_files_ = false;
+    bool only_allow_directory_ = false;
     bool has_selected_file_ = false;
     std::vector<std::string> accept_file_extensions_;
     std::vector<std::string> accept_mime_types_;
@@ -51,6 +53,8 @@
 
   // Whether the input accepts multiple files.
   bool allow_multiple_files;
+  // Whether the input only accepts a directory.
+  bool only_allow_directory;
   // Whether the input already has selected file.
   bool has_selected_file;
   // The file extensions that the input accepts.
@@ -64,6 +68,7 @@
 
  private:
   ChooseFileEvent(bool allow_multiple_files,
+                  bool only_allow_directory,
                   bool has_selected_file,
                   std::vector<std::string> accept_file_extensions,
                   std::vector<std::string> accept_mime_types,
diff --git a/ios/chrome/browser/web/model/choose_file/choose_file_event.mm b/ios/chrome/browser/web/model/choose_file/choose_file_event.mm
index 6ca4c2d..35d365c 100644
--- a/ios/chrome/browser/web/model/choose_file/choose_file_event.mm
+++ b/ios/chrome/browser/web/model/choose_file/choose_file_event.mm
@@ -15,6 +15,12 @@
   return *this;
 }
 
+ChooseFileEvent::Builder& ChooseFileEvent::Builder::SetOnlyAllowDirectory(
+    bool value) {
+  only_allow_directory_ = value;
+  return *this;
+}
+
 ChooseFileEvent::Builder& ChooseFileEvent::Builder::SetHasSelectedFile(
     bool value) {
   has_selected_file_ = value;
@@ -46,19 +52,21 @@
 
 ChooseFileEvent ChooseFileEvent::Builder::Build() {
   CHECK(web_state_);
-  return ChooseFileEvent(allow_multiple_files_, has_selected_file_,
-                         std::move(accept_file_extensions_),
+  return ChooseFileEvent(allow_multiple_files_, only_allow_directory_,
+                         has_selected_file_, std::move(accept_file_extensions_),
                          std::move(accept_mime_types_), web_state_, time_);
 }
 
 ChooseFileEvent::ChooseFileEvent(
     bool allow_multiple_files,
+    bool only_allow_directory,
     bool has_selected_file,
     std::vector<std::string> accept_file_extensions,
     std::vector<std::string> accept_mime_types,
     web::WebState* web_state,
     base::Time time)
     : allow_multiple_files{allow_multiple_files},
+      only_allow_directory{only_allow_directory},
       has_selected_file{has_selected_file},
       accept_file_extensions{std::move(accept_file_extensions)},
       accept_mime_types{std::move(accept_mime_types)},
diff --git a/ios/chrome/browser/web/model/choose_file/choose_file_java_script_feature.mm b/ios/chrome/browser/web/model/choose_file/choose_file_java_script_feature.mm
index 28d574b9..56909386 100644
--- a/ios/chrome/browser/web/model/choose_file/choose_file_java_script_feature.mm
+++ b/ios/chrome/browser/web/model/choose_file/choose_file_java_script_feature.mm
@@ -152,8 +152,11 @@
 
   std::optional<double> accept_type = body_dict.FindDouble("acceptType");
   std::optional<bool> has_multiple = body_dict.FindBool("hasMultiple");
+  std::optional<bool> has_webkitdirectory =
+      body_dict.FindBool("hasWebkitdirectory");
   std::optional<bool> has_selected_file = body_dict.FindBool("hasSelectedFile");
-  if (!accept_type || !has_multiple || !has_selected_file) {
+  if (!accept_type || !has_multiple || !has_webkitdirectory ||
+      !has_selected_file) {
     return;
   }
   int accept_type_int = static_cast<int>(*accept_type);
@@ -177,6 +180,7 @@
     ChooseFileEvent event =
         ChooseFileEvent::Builder()
             .SetAllowMultipleFiles(*has_multiple)
+            .SetOnlyAllowDirectory(*has_webkitdirectory)
             .SetHasSelectedFile(*has_selected_file)
             .SetAcceptFileExtensions(std::move(accept_file_extensions))
             .SetAcceptMimeTypes(std::move(accept_mime_types))
diff --git a/ios/chrome/browser/web/model/choose_file/choose_file_java_script_feature_unittest.mm b/ios/chrome/browser/web/model/choose_file/choose_file_java_script_feature_unittest.mm
index e2228e3..e90518e 100644
--- a/ios/chrome/browser/web/model/choose_file/choose_file_java_script_feature_unittest.mm
+++ b/ios/chrome/browser/web/model/choose_file/choose_file_java_script_feature_unittest.mm
@@ -27,7 +27,7 @@
 const char kPageHtml[] =
     "<html><body>"
     "<input type=\"file\" id=\"choose_file\" "
-    "ACCEPT_PLACEHOLDER MULTIPLE_PLACEHOLDER/>"
+    "ACCEPT_PLACEHOLDER MULTIPLE_PLACEHOLDER WEBKITDIRECTORY_PLACEHOLDER/>"
     "<script>"
     "const input = document.getElementById(\"choose_file\");"
     "SET_FILE_PLACEHOLDER"
@@ -45,6 +45,7 @@
     "  input.type = 'file';"
     "  ACCEPT_PLACEHOLDER"
     "  MULTIPLE_PLACEHOLDER"
+    "  WEBKITDIRECTORY_PLACEHOLDER"
     "  SET_FILE_PLACEHOLDER"
     "  input.click();"
     "}"
@@ -100,7 +101,8 @@
   void LoadHtml(bool has_multiple,
                 bool has_accept,
                 NSString* accept_value,
-                BOOL already_has_file) {
+                BOOL already_has_file,
+                bool has_webkitdirectory) {
     NSString* html = GetParam().test_simulated_click
                          ? @(kPageHtmlForSimulatedClick)
                          : @(kPageHtml);
@@ -114,6 +116,19 @@
       html = [html stringByReplacingOccurrencesOfString:@"MULTIPLE_PLACEHOLDER"
                                              withString:@""];
     }
+    if (has_webkitdirectory) {
+      NSString* replacement =
+          GetParam().test_simulated_click
+              ? @"input.setAttribute('webkitdirectory', true);"
+              : @"webkitdirectory";
+      html = [html
+          stringByReplacingOccurrencesOfString:@"WEBKITDIRECTORY_PLACEHOLDER"
+                                    withString:replacement];
+    } else {
+      html = [html
+          stringByReplacingOccurrencesOfString:@"WEBKITDIRECTORY_PLACEHOLDER"
+                                    withString:@""];
+    }
     if (has_accept) {
       NSString* replacement_template =
           GetParam().test_simulated_click
@@ -163,19 +178,25 @@
   for (int accept_index = 0; accept_index < 9; accept_index++) {
     for (int multiple_index = 0; multiple_index < 2; multiple_index++) {
       for (int has_file_index = 0; has_file_index < 2; has_file_index++) {
-        base::HistogramTester histogram_tester;
-        LoadHtml([bool_attributes[multiple_index] boolValue], true,
-                 accept_attributes[accept_index],
-                 [bool_attributes[has_file_index] boolValue]);
-        ASSERT_TRUE(
-            web::test::TapWebViewElementWithId(web_state(), "choose_file"));
-        histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", 1);
-        histogram_tester.ExpectBucketCount(
-            "IOS.Web.FileInput.Clicked", 2 * accept_index + multiple_index, 1);
-        histogram_tester.ExpectTotalCount("IOS.Web.FileInput.ContentState", 1);
-        histogram_tester.ExpectBucketCount("IOS.Web.FileInput.ContentState",
-                                           2 * multiple_index + has_file_index,
-                                           1);
+        for (int webkitdirectory_index = 0; webkitdirectory_index < 2;
+             webkitdirectory_index++) {
+          base::HistogramTester histogram_tester;
+          LoadHtml([bool_attributes[multiple_index] boolValue], true,
+                   accept_attributes[accept_index],
+                   [bool_attributes[has_file_index] boolValue],
+                   [bool_attributes[webkitdirectory_index] boolValue]);
+          ASSERT_TRUE(
+              web::test::TapWebViewElementWithId(web_state(), "choose_file"));
+          histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", 1);
+          histogram_tester.ExpectBucketCount("IOS.Web.FileInput.Clicked",
+                                             2 * accept_index + multiple_index,
+                                             1);
+          histogram_tester.ExpectTotalCount("IOS.Web.FileInput.ContentState",
+                                            1);
+          histogram_tester.ExpectBucketCount(
+              "IOS.Web.FileInput.ContentState",
+              2 * multiple_index + has_file_index, 1);
+        }
       }
     }
   }
@@ -186,7 +207,7 @@
   base::HistogramTester histogram_tester;
   int total_count = 0;
   // No accept, no multiple
-  LoadHtml(false, false, @"", false);
+  LoadHtml(false, false, @"", false, false);
   total_count++;
   ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", total_count);
@@ -194,7 +215,7 @@
                                      /*kNoAccept*/ 0, 1);
 
   // No accept, multiple
-  LoadHtml(true, false, @"", false);
+  LoadHtml(true, false, @"", false, false);
   total_count++;
   ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", total_count);
@@ -202,7 +223,7 @@
                                      /*kNoAcceptMultiple*/ 1, 1);
 
   // Multiple empty
-  LoadHtml(false, true, @",,,  ,  ,", false);
+  LoadHtml(false, true, @",,,  ,  ,", false, false);
   total_count++;
   ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", total_count);
@@ -210,7 +231,7 @@
                                      /*kNoAccept*/ 0, 2);
 
   // Image extension with a dot
-  LoadHtml(false, true, @".png", false);
+  LoadHtml(false, true, @".png", false, false);
   total_count++;
   ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", total_count);
@@ -218,7 +239,7 @@
                                      /*kImageAccept*/ 6, 1);
 
   // Image extension without a dot
-  LoadHtml(false, true, @"jpg", false);
+  LoadHtml(false, true, @"jpg", false, false);
   total_count++;
   ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", total_count);
@@ -226,7 +247,7 @@
                                      /*kImageAccept*/ 6, 2);
 
   // Image mime
-  LoadHtml(false, true, @"image/png", false);
+  LoadHtml(false, true, @"image/png", false, false);
   total_count++;
   ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", total_count);
@@ -234,7 +255,7 @@
                                      /*kImageAccept*/ 6, 3);
 
   // Generic Image mime
-  LoadHtml(false, true, @"image/*", false);
+  LoadHtml(false, true, @"image/*", false, false);
   total_count++;
   ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", total_count);
@@ -242,7 +263,7 @@
                                      /*kImageAccept*/ 6, 4);
 
   // Two Image types
-  LoadHtml(false, true, @"png, jpg", false);
+  LoadHtml(false, true, @"png, jpg", false, false);
   total_count++;
   ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", total_count);
@@ -250,7 +271,7 @@
                                      /*kImageAccept*/ 6, 5);
 
   // Video with spaces
-  LoadHtml(false, true, @"  .mp4  ", false);
+  LoadHtml(false, true, @"  .mp4  ", false, false);
   total_count++;
   ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", total_count);
@@ -258,7 +279,7 @@
                                      /*kVideoAccept*/ 8, 1);
 
   // Audio without dot
-  LoadHtml(false, true, @"mp3", false);
+  LoadHtml(false, true, @"mp3", false, false);
   total_count++;
   ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", total_count);
@@ -266,7 +287,7 @@
                                      /*kAudioAccept*/ 10, 1);
 
   // Archive
-  LoadHtml(false, true, @"zip , rar ", false);
+  LoadHtml(false, true, @"zip , rar ", false, false);
   total_count++;
   ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", total_count);
@@ -274,7 +295,7 @@
                                      /*kArchiveAccept*/ 12, 1);
 
   // Unknown and image
-  LoadHtml(false, true, @"unknown, jpg", false);
+  LoadHtml(false, true, @"unknown, jpg", false, false);
   total_count++;
   ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", total_count);
@@ -300,7 +321,8 @@
   web::test::ExecuteJavaScriptForFeature(
       web_state_.get(),
       @"window.webkit.messageHandlers['ChooseFileHandler'].postMessage("
-      @"{'acceptType':0,'hasMultiple':true,'hasSelectedFile':false});",
+      @"{'acceptType':0,'hasMultiple':true,'hasSelectedFile':false,"
+      @"'hasWebkitdirectory':false});",
       java_script_feature_.get());
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", 1);
 
@@ -342,7 +364,8 @@
   web::test::ExecuteJavaScriptForFeature(
       web_state_.get(),
       @"window.webkit.messageHandlers['ChooseFileHandler'].postMessage("
-      @"{'acceptType':0,'hasMultiple':true,'hasSelectedFile':false});",
+      @"{'acceptType':0,'hasMultiple':true,'hasSelectedFile':false,"
+      @"'hasWebkitdirectory':false});",
       java_script_feature_.get());
   histogram_tester.ExpectTotalCount("IOS.Web.FileInput.Clicked", 2);
 }
@@ -374,19 +397,22 @@
        accept_attributes_file_extensions) {
     for (const auto& multiple_attribute : bool_attributes) {
       for (const auto& has_file_attributes : bool_attributes) {
-        const bool has_accept = accept_attribute.empty() == false;
-        LoadHtml(multiple_attribute, has_accept, @(accept_attribute.c_str()),
-                 has_file_attributes);
-        ASSERT_TRUE(
-            web::test::TapWebViewElementWithId(web_state(), "choose_file"));
-        const std::optional<ChooseFileEvent> event =
-            ChooseFileEventHolder::GetInstance()->ResetLastChooseFileEvent();
-        ASSERT_TRUE(event.has_value());
-        EXPECT_EQ(expected_file_extensions, event->accept_file_extensions);
-        EXPECT_EQ(multiple_attribute, event->allow_multiple_files);
-        EXPECT_EQ(has_file_attributes, event->has_selected_file);
-        EXPECT_FALSE(
-            ChooseFileEventHolder::GetInstance()->ResetLastChooseFileEvent());
+        for (const auto& only_allow_directory : bool_attributes) {
+          const bool has_accept = accept_attribute.empty() == false;
+          LoadHtml(multiple_attribute, has_accept, @(accept_attribute.c_str()),
+                   has_file_attributes, only_allow_directory);
+          ASSERT_TRUE(
+              web::test::TapWebViewElementWithId(web_state(), "choose_file"));
+          const std::optional<ChooseFileEvent> event =
+              ChooseFileEventHolder::GetInstance()->ResetLastChooseFileEvent();
+          ASSERT_TRUE(event.has_value());
+          EXPECT_EQ(expected_file_extensions, event->accept_file_extensions);
+          EXPECT_EQ(multiple_attribute, event->allow_multiple_files);
+          EXPECT_EQ(only_allow_directory, event->only_allow_directory);
+          EXPECT_EQ(has_file_attributes, event->has_selected_file);
+          EXPECT_FALSE(
+              ChooseFileEventHolder::GetInstance()->ResetLastChooseFileEvent());
+        }
       }
     }
   }
@@ -421,19 +447,22 @@
        accept_attributes_mime_types) {
     for (const auto& multiple_attribute : bool_attributes) {
       for (const auto& has_file_attributes : bool_attributes) {
-        const bool has_accept = accept_attribute.empty() == false;
-        LoadHtml(multiple_attribute, has_accept, @(accept_attribute.c_str()),
-                 has_file_attributes);
-        ASSERT_TRUE(
-            web::test::TapWebViewElementWithId(web_state(), "choose_file"));
-        const std::optional<ChooseFileEvent> event =
-            ChooseFileEventHolder::GetInstance()->ResetLastChooseFileEvent();
-        ASSERT_TRUE(event.has_value());
-        EXPECT_EQ(expected_mime_types, event->accept_mime_types);
-        EXPECT_EQ(multiple_attribute, event->allow_multiple_files);
-        EXPECT_EQ(has_file_attributes, event->has_selected_file);
-        EXPECT_FALSE(
-            ChooseFileEventHolder::GetInstance()->ResetLastChooseFileEvent());
+        for (const auto& only_allow_directory : bool_attributes) {
+          const bool has_accept = accept_attribute.empty() == false;
+          LoadHtml(multiple_attribute, has_accept, @(accept_attribute.c_str()),
+                   has_file_attributes, only_allow_directory);
+          ASSERT_TRUE(
+              web::test::TapWebViewElementWithId(web_state(), "choose_file"));
+          const std::optional<ChooseFileEvent> event =
+              ChooseFileEventHolder::GetInstance()->ResetLastChooseFileEvent();
+          ASSERT_TRUE(event.has_value());
+          EXPECT_EQ(expected_mime_types, event->accept_mime_types);
+          EXPECT_EQ(multiple_attribute, event->allow_multiple_files);
+          EXPECT_EQ(only_allow_directory, event->only_allow_directory);
+          EXPECT_EQ(has_file_attributes, event->has_selected_file);
+          EXPECT_FALSE(
+              ChooseFileEventHolder::GetInstance()->ResetLastChooseFileEvent());
+        }
       }
     }
   }
@@ -447,19 +476,23 @@
   ChooseFileTabHelper* tab_helper =
       ChooseFileTabHelper::FromWebState(web_state());
 
-  LoadHtml(/*has_multiple=*/true, /*has_accept=*/true, @".jpg",
-           /*already_has_file=*/true);
-  ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
+  for (const bool only_allow_directory : {false, true}) {
+    LoadHtml(/*has_multiple=*/true, /*has_accept=*/true, @".jpg",
+             /*already_has_file=*/true, only_allow_directory);
+    ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), "choose_file"));
 
-  EXPECT_FALSE(ChooseFileEventHolder::GetInstance()->HasLastChooseFileEvent());
-  EXPECT_TRUE(tab_helper->HasLastChooseFileEvent());
-  const std::optional<ChooseFileEvent> event =
-      tab_helper->ResetLastChooseFileEvent();
-  ASSERT_TRUE(event.has_value());
-  EXPECT_EQ(std::vector<std::string>{".jpg"}, event->accept_file_extensions);
-  EXPECT_EQ(true, event->allow_multiple_files);
-  EXPECT_EQ(true, event->has_selected_file);
-  EXPECT_FALSE(tab_helper->HasLastChooseFileEvent());
+    EXPECT_FALSE(
+        ChooseFileEventHolder::GetInstance()->HasLastChooseFileEvent());
+    EXPECT_TRUE(tab_helper->HasLastChooseFileEvent());
+    const std::optional<ChooseFileEvent> event =
+        tab_helper->ResetLastChooseFileEvent();
+    ASSERT_TRUE(event.has_value());
+    EXPECT_EQ(std::vector<std::string>{".jpg"}, event->accept_file_extensions);
+    EXPECT_EQ(true, event->allow_multiple_files);
+    EXPECT_EQ(only_allow_directory, event->only_allow_directory);
+    EXPECT_EQ(true, event->has_selected_file);
+    EXPECT_FALSE(tab_helper->HasLastChooseFileEvent());
+  }
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/ios/chrome/browser/web/model/choose_file/choose_file_tab_helper.h b/ios/chrome/browser/web/model/choose_file/choose_file_tab_helper.h
index e2556a0..6504fdd 100644
--- a/ios/chrome/browser/web/model/choose_file/choose_file_tab_helper.h
+++ b/ios/chrome/browser/web/model/choose_file/choose_file_tab_helper.h
@@ -11,6 +11,7 @@
 #import "ios/web/public/web_state_user_data.h"
 
 class ChooseFileController;
+@protocol FileUploadPanelCommands;
 @class WKOpenPanelParameters;
 @class WKFrameInfo;
 
@@ -37,6 +38,9 @@
                          NSString* display_string = nil,
                          UIImage* icon_image = nil);
 
+  // Sets the file upload panel handler.
+  void SetFileUploadPanelHandler(
+      id<FileUploadPanelCommands> file_upload_panel_handler);
   // Displays a file upload panel and calls `completion` with file URLs selected
   // by the user. `parameters` describe the file upload control which initiated
   // the call from `frame`.
@@ -103,6 +107,9 @@
   // Latest `ChooseFileEvent` received from JavaScript.
   std::optional<ChooseFileEvent> last_choose_file_event_;
 
+  // Handler to show/hide the file upload panel UI.
+  __weak id<FileUploadPanelCommands> file_upload_panel_handler_ = nil;
+
   // URLs of files ready to be submitted using `StopChoosingFiles`. This can be
   // used to reuse a local copy for a file instead of downloading it again. The
   // NSObject associated with a given NSURL can represent the version of a file.
diff --git a/ios/chrome/browser/web/model/choose_file/choose_file_tab_helper.mm b/ios/chrome/browser/web/model/choose_file/choose_file_tab_helper.mm
index 1a24498..b26e1eb 100644
--- a/ios/chrome/browser/web/model/choose_file/choose_file_tab_helper.mm
+++ b/ios/chrome/browser/web/model/choose_file/choose_file_tab_helper.mm
@@ -5,8 +5,11 @@
 #import "ios/chrome/browser/web/model/choose_file/choose_file_tab_helper.h"
 
 #import "base/apple/foundation_util.h"
+#import "base/feature_list.h"
 #import "base/files/file_util.h"
 #import "base/task/thread_pool.h"
+#import "ios/chrome/browser/shared/public/commands/file_upload_panel_commands.h"
+#import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/web/model/choose_file/choose_file_controller.h"
 #import "ios/chrome/browser/web/model/choose_file/choose_file_file_utils.h"
 #import "ios/web/public/navigation/navigation_context.h"
@@ -54,14 +57,21 @@
   controller_.reset();
 }
 
+void ChooseFileTabHelper::SetFileUploadPanelHandler(
+    id<FileUploadPanelCommands> file_upload_panel_handler) {
+  file_upload_panel_handler_ = file_upload_panel_handler;
+}
+
 void ChooseFileTabHelper::RunOpenPanel(
     WKOpenPanelParameters* parameters,
     WKFrameInfo* frame,
     base::OnceCallback<void(NSArray<NSURL*>*)> completion)
     API_AVAILABLE(ios(18.4)) {
+  CHECK(base::FeatureList::IsEnabled(kIOSCustomFileUploadMenu));
   // TODO(crbug.com/441659098): Show the open panel and let the user select
   // files.
   std::move(completion).Run(nil);
+  [file_upload_panel_handler_ showFileUploadPanel];
 }
 
 void ChooseFileTabHelper::SetLastChooseFileEvent(ChooseFileEvent event) {
diff --git a/ios/chrome/browser/web/model/choose_file/choose_file_tab_helper_unittest.mm b/ios/chrome/browser/web/model/choose_file/choose_file_tab_helper_unittest.mm
index b4d3140..c181f52 100644
--- a/ios/chrome/browser/web/model/choose_file/choose_file_tab_helper_unittest.mm
+++ b/ios/chrome/browser/web/model/choose_file/choose_file_tab_helper_unittest.mm
@@ -6,13 +6,19 @@
 
 #import <memory>
 
+#import "base/functional/callback_helpers.h"
+#import "base/test/scoped_feature_list.h"
 #import "base/test/task_environment.h"
+#import "ios/chrome/browser/shared/public/commands/file_upload_panel_commands.h"
+#import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/web/model/choose_file/fake_choose_file_controller.h"
 #import "ios/web/public/test/fakes/fake_navigation_context.h"
 #import "ios/web/public/test/fakes/fake_web_state.h"
 #import "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
 #import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/gtest_support.h"
 
 // Test suite for ChooseFileTabHelper.
 class ChooseFileTabHelperTest : public PlatformTest {
@@ -30,6 +36,18 @@
   std::unique_ptr<web::FakeWebState> web_state_;
 };
 
+// Tests that calling `RunOpenPanel()` invokes the file upload panel handler.
+TEST_F(ChooseFileTabHelperTest, RunOpenPanel) {
+  if (@available(iOS 18.4, *)) {
+    base::test::ScopedFeatureList scoped_feature_list{kIOSCustomFileUploadMenu};
+    id handler = OCMProtocolMock(@protocol(FileUploadPanelCommands));
+    OCMExpect([handler showFileUploadPanel]);
+    tab_helper_->SetFileUploadPanelHandler(handler);
+    tab_helper_->RunOpenPanel(nil, nil, base::DoNothing());
+    EXPECT_OCMOCK_VERIFY(handler);
+  }
+}
+
 // Tests that calling `StopChoosingFiles()` submits file selection and that
 // `IsChoosingFiles()` returns false afterwards.
 TEST_F(ChooseFileTabHelperTest, StopChoosingFiles) {
diff --git a/ios/chrome/browser/web/model/choose_file/resources/choose_file_utils.ts b/ios/chrome/browser/web/model/choose_file/resources/choose_file_utils.ts
index 180febd..7865526 100644
--- a/ios/chrome/browser/web/model/choose_file/resources/choose_file_utils.ts
+++ b/ios/chrome/browser/web/model/choose_file/resources/choose_file_utils.ts
@@ -210,6 +210,7 @@
 // Describes the state of a file input which was just clicked.
 interface HtmlInputElementState {
   hasMultiple: boolean;
+  hasWebkitdirectory: boolean;
   acceptType: AcceptType;
   mimeTypes: string;
   fileExtensions: string;
@@ -243,6 +244,7 @@
   acceptString = acceptString ? acceptString : '';
   return {
     hasMultiple: target.hasAttribute('multiple'),
+    hasWebkitdirectory: target.hasAttribute('webkitdirectory'),
     acceptType: accept,
     mimeTypes: parseAcceptAttributeMimeTypes(acceptString),
     fileExtensions: parseAcceptAttributeFileExtensions(acceptString),
diff --git a/ios/chrome/browser/web/model/chrome_main_parts.mm b/ios/chrome/browser/web/model/chrome_main_parts.mm
index 7ce71f17..adc3d6a1 100644
--- a/ios/chrome/browser/web/model/chrome_main_parts.mm
+++ b/ios/chrome/browser/web/model/chrome_main_parts.mm
@@ -10,6 +10,7 @@
 
 #import "base/allocator/partition_alloc_support.h"
 #import "base/check_op.h"
+#import "base/debug/asan_service.h"
 #import "base/feature_list.h"
 #import "base/features.h"
 #import "base/files/file_path.h"
@@ -376,6 +377,13 @@
   segmentation_platform::UkmDatabaseClientHolder::GetClientInstance(nullptr)
       .StartObservation();
 
+  // The AsanService causes ASAN errors to emit additional information. It is
+  // helpful on its own. It is also required by ASAN BackupRefPtr when
+  // reconfiguring PartitionAlloc below.
+#if defined(ADDRESS_SANITIZER)
+  base::debug::AsanService::GetInstance()->Initialize();
+#endif
+
 #if PA_BUILDFLAG(USE_PARTITION_ALLOC)
   base::allocator::PartitionAllocSupport::Get()
       ->ReconfigureAfterFeatureListInit("");
diff --git a/ios/chrome/browser/webauthn/coordinator/BUILD.gn b/ios/chrome/browser/webauthn/coordinator/BUILD.gn
index 5172fda2..b9899b3 100644
--- a/ios/chrome/browser/webauthn/coordinator/BUILD.gn
+++ b/ios/chrome/browser/webauthn/coordinator/BUILD.gn
@@ -13,3 +13,20 @@
   deps = [ "//ios/chrome/browser/webauthn/model" ]
   frameworks = [ "Foundation.framework" ]
 }
+
+source_set("export_coordinator") {
+  sources = [
+    "credential_export_coordinator.h",
+    "credential_export_coordinator.mm",
+    "credential_export_mediator.h",
+    "credential_export_mediator.mm",
+  ]
+  public_deps = [ "//ios/chrome/browser/shared/coordinator/chrome_coordinator" ]
+  deps = [
+    "//components/password_manager/core/browser/ui",
+    "//ios/chrome/browser/webauthn/model",
+    "//ios/chrome/browser/webauthn/ui",
+    "//ui/base",
+  ]
+  frameworks = [ "Foundation.framework" ]
+}
diff --git a/ios/chrome/browser/webauthn/coordinator/credential_export_coordinator.h b/ios/chrome/browser/webauthn/coordinator/credential_export_coordinator.h
new file mode 100644
index 0000000..cafeb27
--- /dev/null
+++ b/ios/chrome/browser/webauthn/coordinator/credential_export_coordinator.h
@@ -0,0 +1,36 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_WEBAUTHN_COORDINATOR_CREDENTIAL_EXPORT_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_WEBAUTHN_COORDINATOR_CREDENTIAL_EXPORT_COORDINATOR_H_
+
+#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h"
+
+namespace password_manager {
+class SavedPasswordsPresenter;
+}  // namespace password_manager
+
+// Coordinator for the credential exchange export flow.
+@interface CredentialExportCoordinator : ChromeCoordinator
+
+// Passing `savedPasswordsPresenter` in the constructor instead of accessing it
+// from the `browser` is not the best practice, but the initialization of this
+// object is quite heavy and it is a common exception in password settings code.
+// TODO(crbug.com/444112223): In case `savedPasswordsPresenter` will end up
+// being used only one time to access credentials, just pass the credentials
+// instead.
+- (instancetype)initWithBaseNavigationController:
+                    (UINavigationController*)navigationController
+                                         browser:(Browser*)browser
+                         savedPasswordsPresenter:
+                             (password_manager::SavedPasswordsPresenter*)
+                                 savedPasswordsPresenter
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_WEBAUTHN_COORDINATOR_CREDENTIAL_EXPORT_COORDINATOR_H_
diff --git a/ios/chrome/browser/webauthn/coordinator/credential_export_coordinator.mm b/ios/chrome/browser/webauthn/coordinator/credential_export_coordinator.mm
new file mode 100644
index 0000000..4838da1
--- /dev/null
+++ b/ios/chrome/browser/webauthn/coordinator/credential_export_coordinator.mm
@@ -0,0 +1,64 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/webauthn/coordinator/credential_export_coordinator.h"
+
+#import "base/memory/raw_ptr.h"
+#import "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
+#import "ios/chrome/browser/webauthn/coordinator/credential_export_mediator.h"
+#import "ios/chrome/browser/webauthn/ui/credential_export_view_controller.h"
+#import "ios/chrome/browser/webauthn/ui/credential_export_view_controller_presentation_delegate.h"
+
+@interface CredentialExportCoordinator () <
+    CredentialExportViewControllerPresentationDelegate>
+@end
+
+@implementation CredentialExportCoordinator {
+  // Displays a view allowing the user to select credentials to export.
+  CredentialExportViewController* _viewController;
+
+  // Handles interaction with the credential export OS libraries.
+  CredentialExportMediator* _mediator;
+
+  // Used to fetch the user's saved passwords for export.
+  raw_ptr<password_manager::SavedPasswordsPresenter> _savedPasswordsPresenter;
+}
+
+@synthesize baseNavigationController = _baseNavigationController;
+
+- (instancetype)initWithBaseNavigationController:
+                    (UINavigationController*)navigationController
+                                         browser:(Browser*)browser
+                         savedPasswordsPresenter:
+                             (password_manager::SavedPasswordsPresenter*)
+                                 savedPasswordsPresenter {
+  self = [super initWithBaseViewController:navigationController
+                                   browser:browser];
+  if (self) {
+    _baseNavigationController = navigationController;
+    _savedPasswordsPresenter = savedPasswordsPresenter;
+  }
+  return self;
+}
+
+- (void)start {
+  _viewController = [[CredentialExportViewController alloc] init];
+  _viewController.delegate = self;
+
+  _mediator = [[CredentialExportMediator alloc]
+               initWithWindow:_baseNavigationController.view.window
+      savedPasswordsPresenter:_savedPasswordsPresenter];
+
+  [_baseNavigationController pushViewController:_viewController animated:YES];
+}
+
+#pragma mark - CredentialExportViewControllerPresentationDelegate
+
+- (void)userDidStartExport {
+  if (@available(iOS 26, *)) {
+    [_mediator startExport];
+  }
+}
+
+@end
diff --git a/ios/chrome/browser/webauthn/coordinator/credential_export_mediator.h b/ios/chrome/browser/webauthn/coordinator/credential_export_mediator.h
new file mode 100644
index 0000000..e3e33b6b
--- /dev/null
+++ b/ios/chrome/browser/webauthn/coordinator/credential_export_mediator.h
@@ -0,0 +1,30 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_WEBAUTHN_COORDINATOR_CREDENTIAL_EXPORT_MEDIATOR_H_
+#define IOS_CHROME_BROWSER_WEBAUTHN_COORDINATOR_CREDENTIAL_EXPORT_MEDIATOR_H_
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+namespace password_manager {
+class SavedPasswordsPresenter;
+}  // namespace password_manager
+
+// Mediator for the credential exchange export flow.
+@interface CredentialExportMediator : NSObject
+
+- (instancetype)initWithWindow:(UIWindow*)window
+       savedPasswordsPresenter:
+           (password_manager::SavedPasswordsPresenter*)savedPasswordsPresenter
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+// Called when the user confirms the export flow.
+- (void)startExport API_AVAILABLE(ios(26.0));
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_WEBAUTHN_COORDINATOR_CREDENTIAL_EXPORT_MEDIATOR_H_
diff --git a/ios/chrome/browser/webauthn/coordinator/credential_export_mediator.mm b/ios/chrome/browser/webauthn/coordinator/credential_export_mediator.mm
new file mode 100644
index 0000000..c118057
--- /dev/null
+++ b/ios/chrome/browser/webauthn/coordinator/credential_export_mediator.mm
@@ -0,0 +1,43 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/webauthn/coordinator/credential_export_mediator.h"
+
+#import "base/memory/raw_ptr.h"
+#import "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
+#import "ios/chrome/browser/webauthn/model/credential_exporter.h"
+
+@implementation CredentialExportMediator {
+  // Used as a presentation anchor for OS views. Must not be nil.
+  UIWindow* _window;
+
+  // Used to fetch the user's saved passwords for export.
+  raw_ptr<password_manager::SavedPasswordsPresenter> _savedPasswordsPresenter;
+
+  // Responsible for interaction with the credential export OS libraries.
+  CredentialExporter* _credentialExporter;
+}
+
+- (instancetype)initWithWindow:(UIWindow*)window
+       savedPasswordsPresenter:
+           (password_manager::SavedPasswordsPresenter*)savedPasswordsPresenter {
+  self = [super init];
+  if (self) {
+    _window = window;
+    _savedPasswordsPresenter = savedPasswordsPresenter;
+  }
+  return self;
+}
+
+#pragma mark - Public
+
+// Called when the user confirms the export flow.
+- (void)startExport {
+  _credentialExporter =
+      [[CredentialExporter alloc] initWithWindow:_window
+                         savedPasswordsPresenter:_savedPasswordsPresenter];
+  [_credentialExporter startExport];
+}
+
+@end
diff --git a/ios/chrome/browser/webauthn/ui/BUILD.gn b/ios/chrome/browser/webauthn/ui/BUILD.gn
new file mode 100644
index 0000000..1fbdce5
--- /dev/null
+++ b/ios/chrome/browser/webauthn/ui/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2025 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("ui") {
+  sources = [
+    "credential_export_view_controller.h",
+    "credential_export_view_controller.mm",
+    "credential_export_view_controller_presentation_delegate.h",
+  ]
+  deps = [
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser/shared/ui/table_view:utils",
+    "//ui/base",
+  ]
+  frameworks = [ "UIKit.framework" ]
+}
diff --git a/ios/chrome/browser/webauthn/ui/credential_export_view_controller.h b/ios/chrome/browser/webauthn/ui/credential_export_view_controller.h
new file mode 100644
index 0000000..29c917a
--- /dev/null
+++ b/ios/chrome/browser/webauthn/ui/credential_export_view_controller.h
@@ -0,0 +1,23 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_WEBAUTHN_UI_CREDENTIAL_EXPORT_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_WEBAUTHN_UI_CREDENTIAL_EXPORT_VIEW_CONTROLLER_H_
+
+#import <UIKit/UIKit.h>
+
+@protocol CredentialExportViewControllerPresentationDelegate;
+
+@interface CredentialExportViewController : UITableViewController
+
+// Delegate for handling dismissal of the view.
+@property(nonatomic, weak)
+    id<CredentialExportViewControllerPresentationDelegate>
+        delegate;
+
+- (instancetype)init;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_WEBAUTHN_UI_CREDENTIAL_EXPORT_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/webauthn/ui/credential_export_view_controller.mm b/ios/chrome/browser/webauthn/ui/credential_export_view_controller.mm
new file mode 100644
index 0000000..4eaadbd
--- /dev/null
+++ b/ios/chrome/browser/webauthn/ui/credential_export_view_controller.mm
@@ -0,0 +1,37 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/webauthn/ui/credential_export_view_controller.h"
+
+#import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
+#import "ios/chrome/browser/webauthn/ui/credential_export_view_controller_presentation_delegate.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ui/base/l10n/l10n_util.h"
+
+@implementation CredentialExportViewController
+
+- (instancetype)init {
+  return [super initWithStyle:ChromeTableViewStyle()];
+}
+
+#pragma mark - UIViewController
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+
+  self.title = l10n_util::GetNSString(IDS_IOS_EXPORT_PASSWORDS_AND_PASSKEYS);
+  self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
+      initWithBarButtonSystemItem:UIBarButtonSystemItemDone
+                           target:self
+                           action:@selector(didTapDone)];
+}
+
+#pragma mark - Actions
+
+- (void)didTapDone {
+  // TODO(crbug.com/449701042): Handle credentials selection
+  [self.delegate userDidStartExport];
+}
+
+@end
diff --git a/ios/chrome/browser/webauthn/ui/credential_export_view_controller_presentation_delegate.h b/ios/chrome/browser/webauthn/ui/credential_export_view_controller_presentation_delegate.h
new file mode 100644
index 0000000..10acf98
--- /dev/null
+++ b/ios/chrome/browser/webauthn/ui/credential_export_view_controller_presentation_delegate.h
@@ -0,0 +1,16 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_WEBAUTHN_UI_CREDENTIAL_EXPORT_VIEW_CONTROLLER_PRESENTATION_DELEGATE_H_
+#define IOS_CHROME_BROWSER_WEBAUTHN_UI_CREDENTIAL_EXPORT_VIEW_CONTROLLER_PRESENTATION_DELEGATE_H_
+
+// Delegate for CredentialExportViewController.
+@protocol CredentialExportViewControllerPresentationDelegate <NSObject>
+
+// Called when the user accepts the export flow.
+- (void)userDidStartExport;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_WEBAUTHN_UI_CREDENTIAL_EXPORT_VIEW_CONTROLLER_PRESENTATION_DELEGATE_H_
diff --git a/ios/chrome/browser/webui/ui_bundled/about/about_ui.cc b/ios/chrome/browser/webui/ui_bundled/about/about_ui.cc
index 0bc8df1f..519d6871 100644
--- a/ios/chrome/browser/webui/ui_bundled/about/about_ui.cc
+++ b/ios/chrome/browser/webui/ui_bundled/about/about_ui.cc
@@ -76,6 +76,7 @@
     web::URLDataSourceIOS::GotDataCallback callback) {
   std::string response;
   // Add your data source here, in alphabetical order.
+  // go/keep-sorted start block=yes
   if (source_name_ == kChromeUICreditsHost) {
     int idr = IDR_ABOUT_UI_CREDITS_HTML;
     if (path == kCreditsJsPath) {
@@ -108,6 +109,7 @@
       response.append("<br><hr><br>");
     }
   }
+  // go/keep-sorted end
 
   FinishDataRequest(response, std::move(callback));
 }
diff --git a/ios/chrome/browser/whats_new/ui/whats_new_screenshot_view_controller.mm b/ios/chrome/browser/whats_new/ui/whats_new_screenshot_view_controller.mm
index ba33a4b75..0136f24 100644
--- a/ios/chrome/browser/whats_new/ui/whats_new_screenshot_view_controller.mm
+++ b/ios/chrome/browser/whats_new/ui/whats_new_screenshot_view_controller.mm
@@ -139,7 +139,7 @@
   LottieAnimationConfiguration* config =
       [[LottieAnimationConfiguration alloc] init];
   config.animationName = animationAssetName;
-  config.loopAnimationCount = 1000;
+  config.shouldLoop = YES;
   return ios::provider::GenerateLottieAnimation(config);
 }
 
diff --git a/ios/chrome/share_extension/extended_share_view_controller.mm b/ios/chrome/share_extension/extended_share_view_controller.mm
index 5f420f9b..42cc693 100644
--- a/ios/chrome/share_extension/extended_share_view_controller.mm
+++ b/ios/chrome/share_extension/extended_share_view_controller.mm
@@ -386,6 +386,7 @@
 }
 
 - (void)moveShareSheet {
+  [self addChildViewController:self.shareSheet];
   [self.view addSubview:self.shareSheet.view];
   [self.shareSheet didMoveToParentViewController:self];
 }
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index b889278..c38ff727 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -1189,7 +1189,12 @@
 }
 
 + (id<GREYMatcher>)swipeActionDeleteButton {
-  NSString* buttonClass = @"_UISwipeActionDynamicButton";
+  NSString* buttonClass;
+  if (iOS26_OR_ABOVE()) {
+    buttonClass = @"_UISwipeActionDynamicButton";
+  } else {
+    buttonClass = @"UISwipeActionStandardButton";
+  }
   return grey_allOf(
       [ChromeMatchersAppInterface
           buttonWithAccessibilityLabelID:IDS_IOS_DELETE_ACTION_TITLE],
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
index 1691308..a2265ecb 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-21715e0042914531de4ec976dabf311ca12f99c3
\ No newline at end of file
+4f19a97a4224141f9aeaa8aee48f83aeae62c9d4
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
index 03012bca..28cec7e 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-bd76444d7527f7d6aefc6279f2dfcee82d2b0330
\ No newline at end of file
+0b1c2483fa1b1cac9c9c7015a8abf1e80a03a96a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index cebf6dc..f8b4e13 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-d74ab4dccf8461c09a0222d481a397007ed6c20b
\ No newline at end of file
+151cb7370a3fa6672bdcf2d177dce9ec1311fbb7
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
index ae6164dc..227ffdc 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-fc5821c787975c394109332001eeda4ac8049ae8
\ No newline at end of file
+0c51c72f44efa654b1e8f08972915f7dbf16850a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index b46cee1..971b994 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-e36ce4dd26163afdb1b7f73e9006fe3eecd7c570
\ No newline at end of file
+7fc4fbccf3bfb2fce605da5a3f12735d79146127
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
index 0f64971..7c2083c 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-871c8fcc233647d6b757dfcea86c21d16f738fbb
\ No newline at end of file
+b11db36e7dd14eeea1156c4e7b2275d69fdbad7e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
index 170993ab..a9594a2 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-f68b9c5112f3752f551123b3056df6bdd0f39961
\ No newline at end of file
+6addd1a36b56012f037411de406ae68a0a9f0872
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
index acabd59..f2cc908 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-369acf1dd02997bdeb4aa9646b00269a72fb1066
\ No newline at end of file
+ddcd61197d1f0c85392dd9dd89f5ad895ad06d66
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
index d28fca3..c9e5285 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-9047f66fc165af0746eff265ff65b781b3643e11
\ No newline at end of file
+eb7c0629773830fc799bbfd8b7778e393f40e1e1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 9bc46143..00b4a118 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-6f232ea0dfbc0a795ff7c88ad02d344de79585bc
\ No newline at end of file
+ae85afe0cfd6755147eebd19cf70a1e259c99802
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 34061bb..6987509 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-82c98f2a8fa29c2e6ab8d110e3ea1c018cc486da
\ No newline at end of file
+8cc71105a943bf8d32112f9561d1a5605012a6a2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
index 94838ea..acbb887d 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-da615f5ac7e4a8c6f2e182261c24e8741fb1ba37
\ No newline at end of file
+4b2bd4c722afb8134baa785bf68cbe78d1ade73b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index c3cb549..720508c7 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-c0544433205078f14431e943c5c3e6acd790d8ae
\ No newline at end of file
+52c00e9597a7daf31fc36cb48f246cbc0ebc7cb5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 6c445f8d..f478d06 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-4c9e708f411871de1abcff465d2847c052545415
\ No newline at end of file
+cfd478707db5f4d0775d9463e1f9aa4f6c93e204
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index afb72c5..20b5a42 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-c16a3e19fb81e49f00ea6605a40dc648ec20dbcb
\ No newline at end of file
+fa8766ef3ee8208cbf0ae69813ce655ec853e811
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
index c64e5b01..15192916 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-a02c0f1d0424ce53984b28f59f2864c3c09b633e
\ No newline at end of file
+233ed7f232f233d86d771b61987c78b1505508be
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 7f95fdd..20a7df2 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-fe2606a91a0df0041862ab3a7c4f9ad4871da2ba
\ No newline at end of file
+b8c8628cd458d0a65413e0df485799c50b2475b5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
index f392397c..0005904 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-fd8474ce5ac78983158521b0b82e383cd8ad5da6
\ No newline at end of file
+0fda1f5e53b33960aba88c10518a21593407fd24
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index a5305be7..08762fe 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e4c749db36d6b24a53bdf6ec709a02a00ca1e6da
\ No newline at end of file
+cfb61f5272e692c608ae4c5e37731d00c72d23ed
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index 43ef452..6351db1 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-7b7f275a5125211d3fccfcd50c229dd627a90fc9
\ No newline at end of file
+01a0b23a8c9300dd6855b69619abdee900942c51
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 21df073..1fd0c04 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-b6c42e1dcc70a19c8c5aeafdc26435bc89bfb9d2
\ No newline at end of file
+26db340c2489fd8901d8019f2fe952c77dc46ff0
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
index dee9054e..30521a1 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-c122111e8c2eea9f2ef5e944a97aa1fc111ca119
\ No newline at end of file
+550598b520af0c8a6ebda9c269fb95d834b944c7
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index fb367578..d739f75 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-cb693b90dae4ea72c92ecd85c88663ac4b117fc1
\ No newline at end of file
+b1fdcad2b1f1835c73eaea91f2afb1e576580568
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
index fdaf678..92d14c4 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-62ef6d4f8b5231f4ffdb4eb2e1ab835bce586429
\ No newline at end of file
+b6039209b4249635e6021c8ab920be0fc41f9e15
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index c6b58dd..8a28368 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-67892a8aa9b56ccc6ac45322150eb01e354154bc
\ No newline at end of file
+adf512a875fbeb17bf55b211cb47bec741d60abd
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
index 7fc2363c..6fa964e7 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-5ac2a03f90f7bd45dfa6bca46d70e92324af8d68
\ No newline at end of file
+c300182e4a12df5f254a745293355efeb2291ae2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index b3c9f92a..1e92398 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-234358fde538011e414575a95d15464cb1604dfe
\ No newline at end of file
+662d417328690d9b71b459212ddd97e7a2c31f8a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
index e8f0d63..8ca70171 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-65709d51b7d1fac36cad623f03b3078f5526085f
\ No newline at end of file
+be6f8c84359f3eea799504e7bb9f91393a32ca9c
\ No newline at end of file
diff --git a/ios/public/provider/chrome/browser/lottie/lottie_animation_configuration.h b/ios/public/provider/chrome/browser/lottie/lottie_animation_configuration.h
index a1e300d3..bd79325 100644
--- a/ios/public/provider/chrome/browser/lottie/lottie_animation_configuration.h
+++ b/ios/public/provider/chrome/browser/lottie/lottie_animation_configuration.h
@@ -12,14 +12,14 @@
 
 // Name of the animation file.
 @property(nonatomic, copy) NSString* animationName;
-// Subdirectory to the json animation file.
-@property(nonatomic, copy) NSString* subdirectory;
 // The bundle in which the animation is located.
 @property(nonatomic, strong) NSBundle* bundle;
-// The loop behavior of the animation.
-@property(nonatomic, assign) CGFloat loopAnimationCount;
 // Whether the animation should loop or not. Default is NO.
-@property(nonatomic, assign,readonly) BOOL shouldLoop;
+@property(nonatomic, assign) BOOL shouldLoop;
+// Whether the main thread should be forced-used to render the animation. This
+// has negatif performance impact on the animation but is required to use some
+// programmatic features (like the gradient). Default is NO.
+@property(nonatomic, assign) BOOL forceUseMainThread;
 
 @end
 
diff --git a/ios/testing/hardware_keyboard_util.mm b/ios/testing/hardware_keyboard_util.mm
index 4c78573..de3215de 100644
--- a/ios/testing/hardware_keyboard_util.mm
+++ b/ios/testing/hardware_keyboard_util.mm
@@ -459,7 +459,11 @@
   PhysicalKeyboardEvent* keyboardEvent =
       [NSClassFromString(@"UIPhysicalKeyboardEvent") _eventWithInput:input
                                                           inputFlags:0];
-  [keyboardEvent _setModifierFlags:flags];
+  if (@available(iOS 17, *)) {
+    [keyboardEvent _setModifierFlags:flags];
+  } else {
+    keyboardEvent._modifierFlags = flags;
+  }
   IOHIDEventRef hidEvent =
       CreateHIDKeyEvent(input, keyboardEvent.timestamp, true);
   [keyboardEvent _setHIDEvent:hidEvent keyboard:0];
diff --git a/ios/web/common/features.h b/ios/web/common/features.h
index 9f2b42c..7e657baab 100644
--- a/ios/web/common/features.h
+++ b/ios/web/common/features.h
@@ -7,8 +7,7 @@
 
 #include "base/feature_list.h"
 
-namespace web {
-namespace features {
+namespace web::features {
 
 // Used to crash the browser if unexpected URL change is detected.
 // https://crbug.com/841105.
@@ -60,13 +59,6 @@
 // fullscreen.
 BASE_DECLARE_FEATURE(kFullscreenScrollThreshold);
 
-// A flag parameter to set the number of pixels to use as the threshold.
-extern const char kFullscreenScrollThresholdAmount[];
-
-// Returns true if SmoothScrollingDefault is disabled and
-// FullscreenScrollThreshold is enabled.
-bool IsFullscreenScrollThresholdEnabled();
-
 // Feature flag that force the use of the synthesized native WKWebView
 // session instead of the (maybe inexistent) saved native session. The
 // purpose of this flag it to allow to testing this code path.
@@ -76,9 +68,6 @@
 // intended to be used as a kill switch.
 BASE_DECLARE_FEATURE(kDetectDestroyedNavigationContexts);
 
-// When true, an option to enable Web Inspector should be present in Settings.
-bool IsWebInspectorSupportEnabled();
-
 // Feature flag to disable the raccoon.
 BASE_DECLARE_FEATURE(kDisableRaccoon);
 
@@ -101,7 +90,17 @@
 // When enabled, JavaScript errors will crash the application.
 BASE_DECLARE_FEATURE(kAssertOnJavaScriptErrors);
 
-}  // namespace features
-}  // namespace web
+// A flag parameter to set the number of pixels to use as the threshold.
+inline constexpr char kFullscreenScrollThresholdAmount[] =
+    "fullscreen_scroll_threshold_amount";
+
+// Returns true if SmoothScrollingDefault is disabled and
+// FullscreenScrollThreshold is enabled.
+bool IsFullscreenScrollThresholdEnabled();
+
+// When true, an option to enable Web Inspector should be present in Settings.
+bool IsWebInspectorSupportEnabled();
+
+}  // namespace web::features
 
 #endif  // IOS_WEB_COMMON_FEATURES_H_
diff --git a/ios/web/common/features.mm b/ios/web/common/features.mm
index 20bbcd2..81a3d87 100644
--- a/ios/web/common/features.mm
+++ b/ios/web/common/features.mm
@@ -7,8 +7,7 @@
 #import "base/metrics/field_trial_params.h"
 #import "build/blink_buildflags.h"
 
-namespace web {
-namespace features {
+namespace web::features {
 
 BASE_FEATURE(kCrashOnUnexpectedURLChange, base::FEATURE_ENABLED_BY_DEFAULT);
 
@@ -52,14 +51,6 @@
 
 BASE_FEATURE(kFullscreenScrollThreshold, base::FEATURE_DISABLED_BY_DEFAULT);
 
-const char kFullscreenScrollThresholdAmount[] =
-    "fullscreen_scroll_threshold_amount";
-
-bool IsFullscreenScrollThresholdEnabled() {
-  return !base::FeatureList::IsEnabled(kSmoothScrollingDefault) &&
-         base::FeatureList::IsEnabled(kFullscreenScrollThreshold);
-}
-
 // This feature will always be disabled and will only be enabled by tests.
 BASE_FEATURE(kForceSynthesizedRestoreSession,
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -67,13 +58,6 @@
 BASE_FEATURE(kDetectDestroyedNavigationContexts,
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-bool IsWebInspectorSupportEnabled() {
-  if (@available(iOS 16.4, *)) {
-    return base::FeatureList::IsEnabled(kEnableWebInspector);
-  }
-  return false;
-}
-
 BASE_FEATURE(kDisableRaccoon, base::FEATURE_DISABLED_BY_DEFAULT);
 
 BASE_FEATURE(kUserAgentBugFixVersion, base::FEATURE_ENABLED_BY_DEFAULT);
@@ -90,5 +74,16 @@
 
 BASE_FEATURE(kAssertOnJavaScriptErrors, base::FEATURE_DISABLED_BY_DEFAULT);
 
-}  // namespace features
-}  // namespace web
+bool IsFullscreenScrollThresholdEnabled() {
+  return !base::FeatureList::IsEnabled(kSmoothScrollingDefault) &&
+         base::FeatureList::IsEnabled(kFullscreenScrollThreshold);
+}
+
+bool IsWebInspectorSupportEnabled() {
+  if (@available(iOS 16.4, *)) {
+    return base::FeatureList::IsEnabled(kEnableWebInspector);
+  }
+  return false;
+}
+
+}  // namespace web::features
diff --git a/ios/web/web_state/ui/crw_web_controller_container_view.mm b/ios/web/web_state/ui/crw_web_controller_container_view.mm
index 0447153..faaf1fa 100644
--- a/ios/web/web_state/ui/crw_web_controller_container_view.mm
+++ b/ios/web/web_state/ui/crw_web_controller_container_view.mm
@@ -45,16 +45,18 @@
                             UIViewAutoresizingFlexibleHeight |
                             UIViewAutoresizingFlexibleTopMargin |
                             UIViewAutoresizingFlexibleLeftMargin;
-    __weak __typeof(self) weakSelf = self;
-    UITraitChangeHandler handler = ^(id<UITraitEnvironment> traitEnvironment,
-                                     UITraitCollection* previousCollection) {
-      [weakSelf updateUIOnTraitChange:previousCollection];
-    };
-    NSArray<UITrait>* traits = @[
-      UITraitVerticalSizeClass.class, UITraitHorizontalSizeClass.class,
-      UITraitPreferredContentSizeCategory.class
-    ];
-    [self registerForTraitChanges:traits withHandler:handler];
+    if (@available(iOS 17, *)) {
+      __weak __typeof(self) weakSelf = self;
+      UITraitChangeHandler handler = ^(id<UITraitEnvironment> traitEnvironment,
+                                       UITraitCollection* previousCollection) {
+        [weakSelf updateUIOnTraitChange:previousCollection];
+      };
+      NSArray<UITrait>* traits = @[
+        UITraitVerticalSizeClass.class, UITraitHorizontalSizeClass.class,
+        UITraitPreferredContentSizeCategory.class
+      ];
+      [self registerForTraitChanges:traits withHandler:handler];
+    }
   }
   return self;
 }
@@ -96,6 +98,17 @@
 
 #pragma mark Layout
 
+#if !defined(__IPHONE_17_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_17_0
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  if (@available(iOS 17, *)) {
+    return;
+  }
+
+  [self updateUIOnTraitChange:previousTraitCollection];
+}
+#endif
+
 - (void)layoutSubviews {
   [super layoutSubviews];
 
diff --git a/ios/web_view/framework/sources.gni b/ios/web_view/framework/sources.gni
index 32579f8..b6df426 100644
--- a/ios/web_view/framework/sources.gni
+++ b/ios/web_view/framework/sources.gni
@@ -106,6 +106,10 @@
   "//ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.mm",
   "//ios/web_view/internal/autofill/web_view_autofill_client_ios.h",
   "//ios/web_view/internal/autofill/web_view_autofill_client_ios.mm",
+  "//ios/web_view/internal/autofill/web_view_autofill_image_fetcher_factory.h",
+  "//ios/web_view/internal/autofill/web_view_autofill_image_fetcher_factory.mm",
+  "//ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl.h",
+  "//ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl.mm",
   "//ios/web_view/internal/autofill/web_view_autofill_log_router_factory.h",
   "//ios/web_view/internal/autofill/web_view_autofill_log_router_factory.mm",
   "//ios/web_view/internal/autofill/web_view_personal_data_manager_factory.h",
@@ -301,6 +305,7 @@
   "//components/component_updater/installer_policies",
   "//components/gcm_driver",
   "//components/history/core/common",
+  "//components/image_fetcher/core",
   "//components/image_fetcher/ios",
   "//components/infobars/core",
   "//components/invalidation",
diff --git a/ios/web_view/internal/DEPS b/ios/web_view/internal/DEPS
index 28ccdab8..5b3420a 100644
--- a/ios/web_view/internal/DEPS
+++ b/ios/web_view/internal/DEPS
@@ -8,6 +8,7 @@
   "+components/gcm_driver",
   "+components/history/core/common",
   "+components/image_fetcher/ios",
+  "+components/image_fetcher/core",
   "+components/infobars/core",
   "+components/invalidation",
   "+components/keyed_service/core",
diff --git a/ios/web_view/internal/autofill/cwv_autofill_prefs.h b/ios/web_view/internal/autofill/cwv_autofill_prefs.h
index 90f90b8f..959d06d6 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_prefs.h
+++ b/ios/web_view/internal/autofill/cwv_autofill_prefs.h
@@ -20,6 +20,9 @@
 inline constexpr char kCWVAutofillVCNUsageEnabled[] =
     "cwv.autofill.vcn_usage_enabled";
 
+inline constexpr char kUseImageFetcherEnabled[] =
+    "cwv.autofill.image_fetcher_usage_enabled";
+
 // Registers the CWVAutofill preferences for this `pref_registry`.
 void RegisterCWVAutofillPrefs(user_prefs::PrefRegistrySyncable* pref_registry);
 
@@ -31,6 +34,10 @@
 
 bool IsAutofillVCNUsageEnabled(const PrefService* prefs);
 
+void SetUseImageFetcherEnabled(PrefService* prefs, bool value);
+
+bool IsUseImageFetcherEnabled(const PrefService* prefs);
+
 }  // namespace ios_web_view
 
 #endif  // IOS_WEB_VIEW_INTERNAL_AUTOFILL_CWV_AUTOFILL_PREFS_H_
diff --git a/ios/web_view/internal/autofill/cwv_autofill_prefs.mm b/ios/web_view/internal/autofill/cwv_autofill_prefs.mm
index 06bb2e6..5576621 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_prefs.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_prefs.mm
@@ -12,6 +12,7 @@
 void RegisterCWVAutofillPrefs(user_prefs::PrefRegistrySyncable* pref_registry) {
   pref_registry->RegisterBooleanPref(kCWVAutofillAddressSyncEnabled, false);
   pref_registry->RegisterBooleanPref(kCWVAutofillVCNUsageEnabled, false);
+  pref_registry->RegisterBooleanPref(kUseImageFetcherEnabled, false);
 }
 
 bool IsAutofillAddressSyncEnabled(const PrefService* prefs) {
@@ -29,4 +30,13 @@
 void SetAutofillVCNUsageEnabled(PrefService* prefs, bool enabled) {
   prefs->SetBoolean(kCWVAutofillVCNUsageEnabled, enabled);
 }
+
+bool IsUseImageFetcherEnabled(const PrefService* prefs) {
+  return prefs->GetBoolean(kUseImageFetcherEnabled);
+}
+
+void SetUseImageFetcherEnabled(PrefService* prefs, bool enabled) {
+  prefs->SetBoolean(kUseImageFetcherEnabled, enabled);
+}
+
 }  // namespace ios_web_view
diff --git a/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_factory.h b/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_factory.h
new file mode 100644
index 0000000..a8f4ad0
--- /dev/null
+++ b/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_factory.h
@@ -0,0 +1,43 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_VIEW_INTERNAL_AUTOFILL_WEB_VIEW_AUTOFILL_IMAGE_FETCHER_FACTORY_H_
+#define IOS_WEB_VIEW_INTERNAL_AUTOFILL_WEB_VIEW_AUTOFILL_IMAGE_FETCHER_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+
+namespace ios_web_view {
+
+class WebViewAutofillImageFetcherImpl;
+
+// Class that owns all AutofillImageFetchers and associates them with
+// BrowserState.
+class WebViewAutofillImageFetcherFactory
+    : public BrowserStateKeyedServiceFactory {
+ public:
+  static WebViewAutofillImageFetcherImpl* GetForBrowserState(
+      web::BrowserState* browser_state);
+
+  static WebViewAutofillImageFetcherFactory* GetInstance();
+
+  WebViewAutofillImageFetcherFactory(
+      const WebViewAutofillImageFetcherFactory&) = delete;
+  WebViewAutofillImageFetcherFactory& operator=(
+      const WebViewAutofillImageFetcherFactory&) = delete;
+
+ private:
+  friend class base::NoDestructor<WebViewAutofillImageFetcherFactory>;
+
+  WebViewAutofillImageFetcherFactory();
+  ~WebViewAutofillImageFetcherFactory() override;
+
+  // BrowserStateKeyedServiceFactory:
+  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
+};
+
+}  // namespace ios_web_view
+
+#endif  // IOS_WEB_VIEW_INTERNAL_AUTOFILL_WEB_VIEW_AUTOFILL_IMAGE_FETCHER_FACTORY_H_
diff --git a/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_factory.mm b/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_factory.mm
new file mode 100644
index 0000000..51467d1
--- /dev/null
+++ b/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_factory.mm
@@ -0,0 +1,51 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web_view/internal/autofill/web_view_autofill_image_fetcher_factory.h"
+
+#import "base/memory/weak_ptr.h"
+#import "base/no_destructor.h"
+#import "components/autofill/core/browser/ui/autofill_image_fetcher.h"
+#import "components/autofill/core/browser/ui/autofill_image_fetcher_base.h"
+#import "components/keyed_service/core/keyed_service.h"
+#import "components/keyed_service/ios/browser_state_dependency_manager.h"
+#import "ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl.h"
+#import "ios/web_view/internal/web_view_browser_state.h"
+#import "services/network/public/cpp/shared_url_loader_factory.h"
+#import "ui/gfx/image/image.h"
+#import "url/gurl.h"
+
+namespace ios_web_view {
+
+// static
+WebViewAutofillImageFetcherImpl*
+WebViewAutofillImageFetcherFactory::GetForBrowserState(
+    web::BrowserState* browser_state) {
+  return static_cast<WebViewAutofillImageFetcherImpl*>(
+      GetInstance()->GetServiceForBrowserState(browser_state, /*create=*/true));
+}
+
+// static
+WebViewAutofillImageFetcherFactory*
+WebViewAutofillImageFetcherFactory::GetInstance() {
+  static base::NoDestructor<WebViewAutofillImageFetcherFactory> instance;
+  return instance.get();
+}
+
+WebViewAutofillImageFetcherFactory::WebViewAutofillImageFetcherFactory()
+    : BrowserStateKeyedServiceFactory(
+          "AutofillImageFetcher",
+          BrowserStateDependencyManager::GetInstance()) {}
+
+WebViewAutofillImageFetcherFactory::~WebViewAutofillImageFetcherFactory() =
+    default;
+
+std::unique_ptr<KeyedService>
+WebViewAutofillImageFetcherFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
+  return std::make_unique<ios_web_view::WebViewAutofillImageFetcherImpl>(
+      context->GetSharedURLLoaderFactory());
+}
+
+}  // namespace ios_web_view
diff --git a/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl.h b/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl.h
new file mode 100644
index 0000000..af21a761
--- /dev/null
+++ b/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl.h
@@ -0,0 +1,61 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_VIEW_INTERNAL_AUTOFILL_WEB_VIEW_AUTOFILL_IMAGE_FETCHER_IMPL_H_
+#define IOS_WEB_VIEW_INTERNAL_AUTOFILL_WEB_VIEW_AUTOFILL_IMAGE_FETCHER_IMPL_H_
+
+#include <Foundation/Foundation.h>
+
+#include "base/memory/raw_ptr.h"
+#include "components/autofill/core/browser/ui/autofill_image_fetcher.h"
+#include "components/image_fetcher/core/image_fetcher_impl.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+}
+
+namespace autofill {
+class AutofillImageFetcher;
+}  // namespace autofill
+
+namespace ios_web_view {
+
+// ChromeWebView implementation of AutofillImageFetcher for iGA clients.
+class WebViewAutofillImageFetcherImpl : public autofill::AutofillImageFetcher,
+                                        public KeyedService {
+ public:
+  explicit WebViewAutofillImageFetcherImpl(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+  ~WebViewAutofillImageFetcherImpl() override;
+
+  // autofill::AutofillImageFetcher:
+  GURL ResolveImageURL(const GURL& image_url,
+                       ImageType image_type) const override;
+  image_fetcher::ImageFetcher* GetImageFetcher() override;
+  base::WeakPtr<autofill::AutofillImageFetcher> GetWeakPtr() override;
+
+  void SetScreenScaleForTesting(CGFloat scale);
+
+ protected:
+  // AutofillImageFetcher:
+  gfx::Image ResolveCardArtImage(const GURL& card_art_url,
+                                 const gfx::Image& card_art_image) override;
+  gfx::Image ResolveValuableImage(const gfx::Image& valuable_image) override;
+
+ private:
+  // The image fetcher attached.
+  image_fetcher::ImageFetcherImpl image_fetcher_;
+
+  // To make sure the fetched images are not blurry, we scale the fetched size
+  // up by the pixel density or 'scale' of the screen. The scale is kept as a
+  // member variable as it can be overridden in tests.
+  CGFloat screen_scale_;
+
+  base::WeakPtrFactory<WebViewAutofillImageFetcherImpl> weak_ptr_factory_{this};
+};
+
+}  // namespace ios_web_view
+
+#endif  // IOS_WEB_VIEW_INTERNAL_AUTOFILL_WEB_VIEW_AUTOFILL_IMAGE_FETCHER_IMPL_H_
diff --git a/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl.mm b/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl.mm
new file mode 100644
index 0000000..c5f5008
--- /dev/null
+++ b/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl.mm
@@ -0,0 +1,141 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl.h"
+
+#import <UIKit/UIKit.h>
+
+#import "base/strings/strcat.h"
+#import "base/strings/stringprintf.h"
+#import "components/autofill/core/browser/payments/constants.h"
+#import "components/autofill/core/browser/ui/autofill_image_fetcher_base.h"
+#import "components/autofill/core/common/autofill_payments_features.h"
+#import "components/image_fetcher/core/image_fetcher_impl.h"
+#import "components/image_fetcher/ios/ios_image_decoder_impl.h"
+#import "services/network/public/cpp/shared_url_loader_factory.h"
+#import "ui/gfx/canvas.h"
+#import "ui/gfx/geometry/rect_f.h"
+#import "ui/gfx/image/image.h"
+#import "ui/gfx/image/image_skia.h"
+#import "ui/gfx/image/image_skia_operations.h"
+#import "url/gurl.h"
+
+namespace ios_web_view {
+
+WebViewAutofillImageFetcherImpl::WebViewAutofillImageFetcherImpl(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    : image_fetcher_(image_fetcher::CreateIOSImageDecoder(),
+                     url_loader_factory),
+      screen_scale_([[UIScreen mainScreen] scale]) {}
+
+WebViewAutofillImageFetcherImpl::~WebViewAutofillImageFetcherImpl() {}
+
+GURL WebViewAutofillImageFetcherImpl::ResolveImageURL(
+    const GURL& image_url,
+    ImageType image_type) const {
+  switch (image_type) {
+    case ImageType::kCreditCardArtImage: {
+      // Some Capital One cards have a static URL rather than 'proper' card art
+      // metadata, and so cannot be fetched at different sizes. We defer
+      // handling that URL to the base class.
+      if (image_url.spec() == autofill::kCapitalOneCardArtUrl) {
+        return image_url;
+      }
+
+      // A FIFE image fetching param suffix is appended to the URL. The image
+      // should be center cropped and of Size(40, 24). For better image quality
+      // we fetch an image based on the screen pixel density, and scale it in
+      // ResolveCardArtImage.
+      const int width = 40 * screen_scale_;
+      const int height = 24 * screen_scale_;
+      GURL::Replacements replacements;
+      std::string path = base::StrCat(
+          {image_url.path(), base::StringPrintf("=w%d-h%d-s", width, height)});
+      replacements.SetPathStr(path);
+      return image_url.ReplaceComponents(replacements);
+    }
+    case ImageType::kPixAccountImage:
+      // Pay with Pix is only queried in Chrome on Android.
+      NOTREACHED();
+    case ImageType::kValuableImage:
+      return image_url;
+  }
+}
+
+image_fetcher::ImageFetcher*
+WebViewAutofillImageFetcherImpl::GetImageFetcher() {
+  return &image_fetcher_;
+}
+
+base::WeakPtr<autofill::AutofillImageFetcher>
+WebViewAutofillImageFetcherImpl::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
+void WebViewAutofillImageFetcherImpl::SetScreenScaleForTesting(
+    CGFloat screen_scale) {
+  screen_scale_ = screen_scale;
+}
+
+gfx::Image WebViewAutofillImageFetcherImpl::ResolveCardArtImage(
+    const GURL& card_art_url,
+    const gfx::Image& card_art_image) {
+  // Some Capital One cards have a static URL rather than 'proper' card art
+  // metadata, and so cannot be fetched at different sizes. We defer handling
+  // those images to the base class.
+  if (card_art_url.spec() == autofill::kCapitalOneCardArtUrl) {
+    return card_art_image;
+  }
+
+  // The image has been fetched at Size(40, 24) * the screen pixel density,
+  // however image_fetcher::IOSImageDecoderImpl creates a UIImage with scale=1
+  // (irregardless of pixel density). We re-scale the UIImage so that it is
+  // 40x24 when rendered, and also apply rounded corners and a border.
+  UIImage* inputImage = card_art_image.ToUIImage();
+  if (!inputImage) {
+    return card_art_image;  // Return original if conversion fails.
+  }
+  UIGraphicsImageRendererFormat* format =
+      [UIGraphicsImageRendererFormat preferredFormat];
+  format.scale = screen_scale_;
+  CGRect drawingRect = CGRectMake(0, 0, 40, 24);
+
+  UIGraphicsImageRenderer* renderer =
+      [[UIGraphicsImageRenderer alloc] initWithSize:drawingRect.size
+                                             format:format];
+  UIImage* outputImage =
+      [renderer imageWithActions:^(UIGraphicsImageRendererContext* context) {
+        // Define corner radius and border width.
+        const CGFloat cornerRadius = 2.0;
+        const CGFloat borderWidth = 1.0;
+        // Create a pth for clipping with rounded corners.
+        UIBezierPath* clipCornersPath =
+            [UIBezierPath bezierPathWithRoundedRect:drawingRect
+                                       cornerRadius:cornerRadius];
+        [clipCornersPath addClip];
+        [inputImage drawInRect:drawingRect
+                     blendMode:kCGBlendModeNormal
+                         alpha:1.0];
+
+        // Draw a border inside the clipped area. The border is 1dp wide, with
+        // rounded corners of 2dp, using Grey 300.
+        [[UIColor systemGray3Color] setStroke];
+        CGContextSetLineWidth(context.CGContext, borderWidth);
+        CGRect borderRect =
+            CGRectInset(drawingRect, borderWidth / 2, borderWidth / 2);
+        UIBezierPath* borderPath =
+            [UIBezierPath bezierPathWithRoundedRect:borderRect
+                                       cornerRadius:cornerRadius];
+        [borderPath stroke];
+      }];
+
+  return gfx::Image(outputImage);
+}
+
+gfx::Image WebViewAutofillImageFetcherImpl::ResolveValuableImage(
+    const gfx::Image& valuable_image) {
+  return valuable_image;
+}
+
+}  // namespace ios_web_view
diff --git a/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl_unittest.mm b/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl_unittest.mm
new file mode 100644
index 0000000..fa56b05
--- /dev/null
+++ b/ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl_unittest.mm
@@ -0,0 +1,86 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl.h"
+
+#import <UIKit/UIKit.h>
+
+#import "base/strings/string_number_conversions.h"
+#import "base/strings/string_split.h"
+#import "components/autofill/core/browser/data_model/payments/credit_card.h"
+#import "components/autofill/core/browser/ui/autofill_image_fetcher_base.h"
+#import "services/network/public/cpp/shared_url_loader_factory.h"
+#import "testing/gtest/include/gtest/gtest.h"
+#import "testing/platform_test.h"
+#import "ui/base/l10n/l10n_util_mac.h"
+#import "ui/base/resource/resource_bundle.h"
+#import "url/gurl.h"
+
+namespace ios_web_view {
+
+class WebViewAutofillImageFetcherImplTest : public PlatformTest {
+ public:
+  WebViewAutofillImageFetcherImplTest()
+      : autofill_image_fetcher_impl_(nullptr) {
+    ui::ResourceBundle::InitSharedInstanceWithLocale(
+        l10n_util::GetLocaleOverride(), /*delegate=*/nullptr,
+        ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES);
+  }
+
+  WebViewAutofillImageFetcherImpl* autofill_image_fetcher() {
+    return &autofill_image_fetcher_impl_;
+  }
+
+  ~WebViewAutofillImageFetcherImplTest() override {
+    ui::ResourceBundle::CleanupSharedInstance();
+  }
+
+ private:
+  WebViewAutofillImageFetcherImpl autofill_image_fetcher_impl_;
+};
+
+TEST_F(WebViewAutofillImageFetcherImplTest, ResolveCardArtURL) {
+  // ResolveImageURL should append FIFE parameters, specifying an image that is
+  // 40x24px scaled to the screen scale.
+  autofill_image_fetcher()->SetScreenScaleForTesting(4);
+  EXPECT_EQ(
+      autofill_image_fetcher()->ResolveImageURL(
+          GURL("https://www.example.com/fake_image1"),
+          autofill::AutofillImageFetcherBase::ImageType::kCreditCardArtImage),
+      GURL("https://www.example.com/fake_image1=w160-h96-s"));
+}
+
+TEST_F(WebViewAutofillImageFetcherImplTest, ResolveCardArtImage) {
+  // On iOS, the underlying decoder for the image fetcher always decodes
+  // into a scale=1 UIImage. ResolveImage for credit card images then re-scales
+  // it to match the screen scale.
+  //
+  // For this test, we mimic this by creating a UIImage of scale 1 directly,
+  // then making sure that ResolveImage re-scales it to the mocked screen scale
+  // set on the WebViewAutofillImageFetcherImpl.
+  UIImage* input_image =
+      ui::ResourceBundle::GetSharedInstance()
+          .GetNativeImageNamed(autofill::CreditCard::IconResourceId("visaCC"))
+          .ToUIImage();
+  input_image = [UIImage imageWithCGImage:[input_image CGImage]
+                                    scale:1
+                              orientation:input_image.imageOrientation];
+  ASSERT_EQ(input_image.scale, 1);
+
+  autofill_image_fetcher()->SetScreenScaleForTesting(7);
+  gfx::Image card_art_image = autofill_image_fetcher()->ResolveImage(
+      GURL("https://example.com/fake_image1"), gfx::Image(input_image),
+      autofill::AutofillImageFetcherBase::ImageType::kCreditCardArtImage);
+  EXPECT_EQ(card_art_image.ToUIImage().scale, 7);
+}
+
+// Server can return an empty image.
+TEST_F(WebViewAutofillImageFetcherImplTest, ResolveCardArtImage_EmptyImage) {
+  gfx::Image resolved_image = autofill_image_fetcher()->ResolveImage(
+      GURL("https://example.com/fake_image1"), gfx::Image(),
+      autofill::AutofillImageFetcherBase::ImageType::kCreditCardArtImage);
+  EXPECT_TRUE(resolved_image.IsEmpty());
+}
+
+}  // namespace ios_web_view
diff --git a/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.mm b/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.mm
index fa3394c..6b80819 100644
--- a/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.mm
+++ b/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.mm
@@ -12,6 +12,9 @@
 #import "components/keyed_service/core/service_access_type.h"
 #import "components/keyed_service/ios/browser_state_dependency_manager.h"
 #import "ios/web_view/internal/app/application_context.h"
+#import "ios/web_view/internal/autofill/cwv_autofill_prefs.h"
+#import "ios/web_view/internal/autofill/web_view_autofill_image_fetcher_factory.h"
+#import "ios/web_view/internal/autofill/web_view_autofill_image_fetcher_impl.h"
 #import "ios/web_view/internal/signin/web_view_identity_manager_factory.h"
 #import "ios/web_view/internal/sync/web_view_sync_service_factory.h"
 #import "ios/web_view/internal/web_view_browser_state.h"
@@ -41,6 +44,7 @@
   DependsOn(WebViewIdentityManagerFactory::GetInstance());
   DependsOn(WebViewWebDataServiceWrapperFactory::GetInstance());
   DependsOn(WebViewSyncServiceFactory::GetInstance());
+  DependsOn(WebViewAutofillImageFetcherFactory::GetInstance());
 }
 
 WebViewPersonalDataManagerFactory::~WebViewPersonalDataManagerFactory() {}
@@ -58,6 +62,12 @@
           browser_state, ServiceAccessType::EXPLICIT_ACCESS);
   auto* sync_service =
       WebViewSyncServiceFactory::GetForBrowserState(browser_state);
+
+  PrefService* prefs = browser_state->GetPrefs();
+  if (prefs->GetBoolean(ios_web_view::kUseImageFetcherEnabled)) {
+    // TODO(crbug.com/448641522): Create ImageFetcher and pass to
+    // PersonalDataManager.
+  }
   return std::make_unique<autofill::PersonalDataManager>(
       profile_db, account_db, browser_state->GetPrefs(),
       ApplicationContext::GetInstance()->GetLocalState(),
diff --git a/ios/web_view/internal/cwv_preferences.mm b/ios/web_view/internal/cwv_preferences.mm
index 74fb1ad..4f5bee3 100644
--- a/ios/web_view/internal/cwv_preferences.mm
+++ b/ios/web_view/internal/cwv_preferences.mm
@@ -65,6 +65,14 @@
   return ios_web_view::IsAutofillAddressSyncEnabled(_prefService);
 }
 
+- (void)setUseImageFetcherEnabled:(BOOL)enabled {
+  ios_web_view::SetUseImageFetcherEnabled(_prefService, enabled);
+}
+
+- (BOOL)isUseImageFetcherEnabled {
+  return ios_web_view::IsUseImageFetcherEnabled(_prefService);
+}
+
 - (void)setPasswordAffiliationEnabled:(BOOL)enabled {
   ios_web_view::SetPasswordAffiliationEnabled(_prefService, enabled);
 }
diff --git a/ios/web_view/internal/cwv_preferences_unittest.mm b/ios/web_view/internal/cwv_preferences_unittest.mm
index f76a549..3b782a0 100644
--- a/ios/web_view/internal/cwv_preferences_unittest.mm
+++ b/ios/web_view/internal/cwv_preferences_unittest.mm
@@ -61,6 +61,8 @@
         ios_web_view::kCWVPasswordAffiliationEnabled, false);
     pref_registry->RegisterBooleanPref(
         ios_web_view::kCWVAutofillVCNUsageEnabled, false);
+    pref_registry->RegisterBooleanPref(ios_web_view::kUseImageFetcherEnabled,
+                                       false);
 
     base::FilePath temp_dir_path;
     EXPECT_TRUE(base::PathService::Get(base::DIR_TEMP, &temp_dir_path));
@@ -141,6 +143,16 @@
   EXPECT_TRUE(preferences.autofillAddressSyncEnabled);
 }
 
+// Tests CWVPreferences `useImageFetcherEnabled`.
+TEST_F(CWVPreferencesTest, UseImageFetcherEnabled) {
+  std::unique_ptr<PrefService> pref_service = CreateTestPrefService();
+  CWVPreferences* preferences =
+      [[CWVPreferences alloc] initWithPrefService:pref_service.get()];
+  EXPECT_FALSE(preferences.useImageFetcherEnabled);
+  preferences.useImageFetcherEnabled = YES;
+  EXPECT_TRUE(preferences.useImageFetcherEnabled);
+}
+
 // Tests CWVPreferences `passwordAffiliationEnabled`.
 TEST_F(CWVPreferencesTest, PasswordAffiliationEnabled) {
   std::unique_ptr<PrefService> pref_service = CreateTestPrefService();
diff --git a/ios/web_view/public/cwv_defines.h b/ios/web_view/public/cwv_defines.h
index a16a389b..dbcc6e7 100644
--- a/ios/web_view/public/cwv_defines.h
+++ b/ios/web_view/public/cwv_defines.h
@@ -111,6 +111,9 @@
 // Supports AccountAddressSyncing
 #define IOS_WEB_VIEW_SUPPORTS_ACCOUNT_ADDRESS_SYNC 1
 
+// Supports using Image Fetcher.
+#define IOS_WEB_VIEW_SUPPORTS_USING_IMAGE_FETCHER 1
+
 // Supports CWVGlobalState.
 #define IOS_WEB_VIEW_SUPPORTS_GLOBAL_STATE 1
 
diff --git a/ios/web_view/public/cwv_preferences.h b/ios/web_view/public/cwv_preferences.h
index 2ee693cd..fcce9d2 100644
--- a/ios/web_view/public/cwv_preferences.h
+++ b/ios/web_view/public/cwv_preferences.h
@@ -67,6 +67,10 @@
 @property(nonatomic, assign, getter=isAutofillVCNUsageEnabled)
     BOOL autofillVCNUsageEnabled;
 
+// Whether or not image fetcher usage is enabled.
+@property(nonatomic, assign, getter=isUseImageFetcherEnabled)
+    BOOL useImageFetcherEnabled;
+
 - (instancetype)init NS_UNAVAILABLE;
 
 // Resets all translation settings back to default. In particular, this will
diff --git a/ios/web_view/test/BUILD.gn b/ios/web_view/test/BUILD.gn
index 4c2b4f9..baa7acc 100644
--- a/ios/web_view/test/BUILD.gn
+++ b/ios/web_view/test/BUILD.gn
@@ -66,6 +66,7 @@
     "../internal/autofill/cwv_credit_card_unittest.mm",
     "../internal/autofill/cwv_credit_card_verifier_unittest.mm",
     "../internal/autofill/cwv_vcn_enrollment_manager_unittest.mm",
+    "../internal/autofill/web_view_autofill_image_fetcher_impl_unittest.mm",
     "../internal/cwv_download_task_unittest.mm",
     "../internal/cwv_favicon_unittest.mm",
     "../internal/cwv_flags_unittest.mm",
diff --git a/ios_internal b/ios_internal
index d32e8bd..221cebd 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit d32e8bd0adb36c52d410b11234c3d94cf79a1859
+Subproject commit 221cebde5272f8a5404872c955eae9fe0251e1c2
diff --git a/ipc/ipc_channel.h b/ipc/ipc_channel.h
index c0747885..43a224a 100644
--- a/ipc/ipc_channel.h
+++ b/ipc/ipc_channel.h
@@ -190,13 +190,6 @@
   // NOTE: Not all implementations support this.
   virtual AssociatedInterfaceSupport* GetAssociatedInterfaceSupport() = 0;
 
-  // Overridden from ipc::Sender.
-  // Send a message over the Channel to the listener on the other end.
-  //
-  // |message| must be allocated using operator new.  This object will be
-  // deleted once the contents of the Message have been sent.
-  bool Send(Message* message) override = 0;
-
   // Sets the UrgentMessageObserver for this channel. `observer` must outlive
   // the channel.
   //
diff --git a/ipc/ipc_channel_mojo.cc b/ipc/ipc_channel_mojo.cc
index f2f52c8..05bb174 100644
--- a/ipc/ipc_channel_mojo.cc
+++ b/ipc/ipc_channel_mojo.cc
@@ -189,26 +189,6 @@
   }
 }
 
-bool ChannelMojo::Send(Message* message) {
-  DVLOG(2) << "sending message @" << message << " on channel @" << this
-           << " with type " << message->type();
-
-  std::unique_ptr<Message> scoped_message = base::WrapUnique(message);
-  if (!message_reader_)
-    return false;
-
-  // Comment copied from ipc_channel_posix.cc:
-  // We can't close the pipe here, because calling OnChannelError may destroy
-  // this object, and that would be bad if we are called from Send(). Instead,
-  // we return false and hope the caller will close the pipe. If they do not,
-  // the pipe will still be closed next time OnFileCanReadWithoutBlocking is
-  // called.
-  //
-  // With Mojo, there's no OnFileCanReadWithoutBlocking, but we expect the
-  // pipe's connection error handler will be invoked in its place.
-  return message_reader_->Send(std::move(scoped_message));
-}
-
 Channel::AssociatedInterfaceSupport*
 ChannelMojo::GetAssociatedInterfaceSupport() { return this; }
 
@@ -225,17 +205,8 @@
   listener_->OnChannelConnected(peer_pid);
 }
 
-void ChannelMojo::OnMessageReceived(const Message& message) {
-  const Message* message_ptr = &message;
-  TRACE_IPC_MESSAGE_SEND("ipc,toplevel", "ChannelMojo::OnMessageReceived",
-                         message_ptr);
-  listener_->OnMessageReceived(message);
-  if (message.dispatch_error())
-    listener_->OnBadMessageReceived(message);
-}
-
 void ChannelMojo::OnBrokenDataReceived() {
-  listener_->OnBadMessageReceived(Message());
+  listener_->OnBadMessageReceived();
 }
 
 void ChannelMojo::AddGenericAssociatedInterface(
diff --git a/ipc/ipc_channel_mojo.h b/ipc/ipc_channel_mojo.h
index bd38a48..d46bec1 100644
--- a/ipc/ipc_channel_mojo.h
+++ b/ipc/ipc_channel_mojo.h
@@ -50,13 +50,11 @@
   void Unpause(bool flush) override;
   void Flush() override;
   void Close() override;
-  bool Send(Message* message) override;
   Channel::AssociatedInterfaceSupport* GetAssociatedInterfaceSupport() override;
   void SetUrgentMessageObserver(UrgentMessageObserver* observer) override;
 
   // MessagePipeReader::Delegate
   void OnPeerPidReceived(int32_t peer_pid) override;
-  void OnMessageReceived(const Message& message) override;
   void OnBrokenDataReceived() override;
   void OnPipeError() override;
   void OnAssociatedInterfaceRequest(
diff --git a/ipc/ipc_channel_mojo_unittest.cc b/ipc/ipc_channel_mojo_unittest.cc
index dd47499..015bbd0 100644
--- a/ipc/ipc_channel_mojo_unittest.cc
+++ b/ipc/ipc_channel_mojo_unittest.cc
@@ -212,8 +212,6 @@
   explicit ListenerThatQuits(base::OnceClosure quit_closure)
       : quit_closure_(std::move(quit_closure)) {}
 
-  bool OnMessageReceived(const IPC::Message& message) override { return true; }
-
   void OnChannelConnected(int32_t peer_pid) override {
     std::move(quit_closure_).Run();
   }
@@ -328,11 +326,7 @@
   std::unique_ptr<ChannelProxyRunner> runner_;
 };
 
-class DummyListener : public IPC::Listener {
- public:
-  // IPC::Listener
-  bool OnMessageReceived(const IPC::Message& message) override { return true; }
-};
+class DummyListener : public IPC::Listener {};
 
 class ListenerWithIndirectProxyAssociatedInterface
     : public IPC::Listener,
@@ -343,8 +337,6 @@
   ~ListenerWithIndirectProxyAssociatedInterface() override = default;
 
   // IPC::Listener:
-  bool OnMessageReceived(const IPC::Message& message) override { return true; }
-
   void OnAssociatedInterfaceRequest(
       const std::string& interface_name,
       mojo::ScopedInterfaceEndpointHandle handle) override {
@@ -452,7 +444,6 @@
  public:
   AssociatedInterfaceDroppingListener(base::OnceClosure callback)
       : callback_(std::move(callback)) {}
-  bool OnMessageReceived(const IPC::Message& message) override { return false; }
 
   void OnAssociatedInterfaceRequest(
       const std::string& interface_name,
@@ -488,8 +479,6 @@
     EXPECT_EQ(peer_pid, kMagicChildId);
     RunQuitClosure();
   }
-
-  bool OnMessageReceived(const IPC::Message& message) override { NOTREACHED(); }
 };
 
 // The global PID is only used on systems that use the zygote. Hence, this
@@ -592,8 +581,6 @@
   }
 
   // IPC::Listener:
-  bool OnMessageReceived(const IPC::Message& message) override { return true; }
-
   void OnAssociatedInterfaceRequest(
       const std::string& interface_name,
       mojo::ScopedInterfaceEndpointHandle handle) override {
diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc
index 6233c095..55b22dff 100644
--- a/ipc/ipc_channel_proxy.cc
+++ b/ipc/ipc_channel_proxy.cc
@@ -72,10 +72,6 @@
   }
 }
 
-bool ChannelProxy::Context::TryFilters(const Message& message) {
-  return false;
-}
-
 // Called on the IPC::Channel thread
 void ChannelProxy::Context::PauseChannel() {
   DCHECK(channel_);
@@ -95,22 +91,6 @@
 }
 
 // Called on the IPC::Channel thread
-bool ChannelProxy::Context::OnMessageReceived(const Message& message) {
-  // First give a chance to the filters to process this message.
-  if (!TryFilters(message))
-    OnMessageReceivedNoFilter(message);
-  return true;
-}
-
-// Called on the IPC::Channel thread
-bool ChannelProxy::Context::OnMessageReceivedNoFilter(const Message& message) {
-  GetTaskRunner(message.routing_id())
-      ->PostTask(FROM_HERE,
-                 base::BindOnce(&Context::OnDispatchMessage, this, message));
-  return true;
-}
-
-// Called on the IPC::Channel thread
 void ChannelProxy::Context::OnChannelConnected(int32_t peer_pid) {
   // We cache off the peer_pid so it can be safely accessed from both threads.
   {
@@ -171,29 +151,6 @@
   listener_ = nullptr;
 }
 
-// Called on the IPC::Channel thread
-void ChannelProxy::Context::OnSendMessage(std::unique_ptr<Message> message) {
-  if (!channel_) {
-    OnChannelClosed();
-    return;
-  }
-
-  if (!channel_->Send(message.release()))
-    OnChannelError();
-}
-
-// Called on the listener's thread
-void ChannelProxy::Context::OnDispatchMessage(const Message& message) {
-  if (!listener_)
-    return;
-
-  OnDispatchConnected();
-
-  listener_->OnMessageReceived(message);
-  if (message.dispatch_error())
-    listener_->OnBadMessageReceived(message);
-}
-
 // Called on the IPC::Channel thread.
 scoped_refptr<base::SingleThreadTaskRunner>
 ChannelProxy::Context::GetTaskRunner(int32_t routing_id) {
@@ -223,9 +180,9 @@
 }
 
 // Called on the listener's thread
-void ChannelProxy::Context::OnDispatchBadMessage(const Message& message) {
+void ChannelProxy::Context::OnDispatchBadMessage() {
   if (listener_)
-    listener_->OnBadMessageReceived(message);
+    listener_->OnBadMessageReceived();
 }
 
 // Called on the listener's thread
@@ -256,12 +213,6 @@
     support->AddGenericAssociatedInterface(name, factory);
 }
 
-void ChannelProxy::Context::Send(Message* message) {
-  ipc_task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&ChannelProxy::Context::OnSendMessage, this,
-                                base::WrapUnique(message)));
-}
-
 // Called on the listener's thread.
 void ChannelProxy::Context::SetUrgentMessageObserver(
     UrgentMessageObserver* observer) {
@@ -379,21 +330,6 @@
   }
 }
 
-bool ChannelProxy::Send(Message* message) {
-  DCHECK(!message->is_sync()) << "Need to use IPC::SyncChannel";
-  SendInternal(message);
-  return true;
-}
-
-void ChannelProxy::SendInternal(Message* message) {
-  DCHECK(did_init_);
-
-  // TODO(alexeypa): add DCHECK(CalledOnValidThread()) here. Currently there are
-  // tests that call Send() from a wrong thread. See http://crbug.com/163523.
-
-  context_->Send(message);
-}
-
 void ChannelProxy::AddGenericAssociatedInterfaceForIOThread(
     const std::string& name,
     const GenericAssociatedInterfaceFactory& factory) {
diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h
index a8e2b9f..a046af7a 100644
--- a/ipc/ipc_channel_proxy.h
+++ b/ipc/ipc_channel_proxy.h
@@ -129,10 +129,6 @@
   // call this method multiple times.  Redundant calls are ignored.
   void Close();
 
-  // Send a message asynchronously.  The message is routed to the background
-  // thread where it is passed to the IPC::Channel's Send method.
-  bool Send(Message* message) override;
-
   // Set the `UrgentMessageObserver` for the channel. Must be called on the
   // proxy thread before initialization.
   void SetUrgentMessageObserver(UrgentMessageObserver* observer);
@@ -228,12 +224,6 @@
       return default_listener_task_runner_;
     }
 
-    // Dispatches a message on the listener thread.
-    void OnDispatchMessage(const Message& message);
-
-    // Sends |message| from appropriate thread.
-    void Send(Message* message);
-
     // Called on the IPC::Channel thread.
     // Returns the task runner associated with |routing_id|.
     scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
@@ -244,20 +234,12 @@
     ~Context() override;
 
     // IPC::Listener methods:
-    bool OnMessageReceived(const Message& message) override;
     void OnChannelConnected(int32_t peer_pid) override;
     void OnChannelError() override;
     void OnAssociatedInterfaceRequest(
         const std::string& interface_name,
         mojo::ScopedInterfaceEndpointHandle handle) override;
 
-    // Like OnMessageReceived but doesn't try the filters.
-    bool OnMessageReceivedNoFilter(const Message& message);
-
-    // Gives the filters a chance at processing |message|.
-    // Returns true if the message was processed, false otherwise.
-    bool TryFilters(const Message& message);
-
     void PauseChannel();
     void UnpauseChannel(bool flush);
     void FlushChannel();
@@ -277,13 +259,10 @@
     // Create the Channel
     void CreateChannel(std::unique_ptr<ChannelFactory> factory);
 
-    // Methods called on the IO thread.
-    void OnSendMessage(std::unique_ptr<Message> message_ptr);
-
     // Methods called on the listener thread.
     void OnDispatchConnected();
     void OnDispatchError();
-    void OnDispatchBadMessage(const Message& message);
+    void OnDispatchBadMessage();
     void OnDispatchAssociatedInterfaceRequest(
         const std::string& interface_name,
         mojo::ScopedInterfaceEndpointHandle handle);
@@ -341,9 +320,6 @@
 
   bool did_init() const { return did_init_; }
 
-  // A Send() which doesn't DCHECK if the message is synchronous.
-  void SendInternal(Message* message);
-
  private:
   template <typename Interface>
   static void BindPendingAssociatedReceiver(
diff --git a/ipc/ipc_listener.cc b/ipc/ipc_listener.cc
index 29020da..c4cccde 100644
--- a/ipc/ipc_listener.cc
+++ b/ipc/ipc_listener.cc
@@ -6,10 +6,6 @@
 
 namespace IPC {
 
-bool Listener::OnMessageReceived(const Message& message) {
-  return false;
-}
-
 std::string Listener::ToDebugString() {
   return "IPC::Listener";
 }
diff --git a/ipc/ipc_listener.h b/ipc/ipc_listener.h
index 1b922ac8..3af55d0 100644
--- a/ipc/ipc_listener.h
+++ b/ipc/ipc_listener.h
@@ -15,15 +15,9 @@
 
 namespace IPC {
 
-class Message;
-
 // Implemented by consumers of a Channel to receive messages.
 class COMPONENT_EXPORT(IPC) Listener {
  public:
-  // Called when a message is received.  Returns true iff the message was
-  // handled. Default implementation rejects all messages.
-  virtual bool OnMessageReceived(const Message& message);
-
   // Called when the channel is connected and we have received the internal
   // Hello message from the peer.
   virtual void OnChannelConnected(int32_t peer_pid) {}
@@ -33,7 +27,7 @@
   virtual void OnChannelError() {}
 
   // Called when a message's deserialization failed.
-  virtual void OnBadMessageReceived(const Message& message) {}
+  virtual void OnBadMessageReceived() {}
 
   // Called when an associated interface request is received on a Channel and
   // the Channel has no registered handler for it.
diff --git a/ipc/ipc_message_pipe_reader.cc b/ipc/ipc_message_pipe_reader.cc
index 94b995d..c371862a 100644
--- a/ipc/ipc_message_pipe_reader.cc
+++ b/ipc/ipc_message_pipe_reader.cc
@@ -105,10 +105,6 @@
     receiver_.reset();
 }
 
-bool MessagePipeReader::Send(std::unique_ptr<Message> message) {
-  return false;
-}
-
 void MessagePipeReader::GetRemoteInterface(
     mojo::GenericPendingAssociatedReceiver receiver) {
   if (!sender_.is_bound())
diff --git a/ipc/ipc_message_pipe_reader.h b/ipc/ipc_message_pipe_reader.h
index 9ae7b63f..706c8ca 100644
--- a/ipc/ipc_message_pipe_reader.h
+++ b/ipc/ipc_message_pipe_reader.h
@@ -51,7 +51,6 @@
   class Delegate {
    public:
     virtual void OnPeerPidReceived(int32_t peer_pid) = 0;
-    virtual void OnMessageReceived(const Message& message) = 0;
     virtual void OnBrokenDataReceived() = 0;
     virtual void OnPipeError() = 0;
     virtual void OnAssociatedInterfaceRequest(
@@ -87,10 +86,6 @@
   // Return true if the MessagePipe is alive.
   bool IsValid() { return sender_.is_bound(); }
 
-  // Sends an IPC::Message to the other end of the pipe. Safe to call from any
-  // thread.
-  bool Send(std::unique_ptr<Message> message);
-
   // Requests an associated interface from the other end of the pipe.
   void GetRemoteInterface(mojo::GenericPendingAssociatedReceiver receiver);
 
diff --git a/ipc/ipc_sender.h b/ipc/ipc_sender.h
index 952ef35..c105f71e 100644
--- a/ipc/ipc_sender.h
+++ b/ipc/ipc_sender.h
@@ -9,16 +9,7 @@
 
 namespace IPC {
 
-class Message;
-
 class COMPONENT_EXPORT(IPC) Sender {
- public:
-  // Sends the given IPC message.  The implementor takes ownership of the
-  // given Message regardless of whether or not this method succeeds.  This
-  // is done to make this method easier to use.  Returns true on success and
-  // false otherwise.
-  virtual bool Send(Message* msg) = 0;
-
  protected:
   virtual ~Sender() {}
 };
diff --git a/ipc/ipc_sync_channel.cc b/ipc/ipc_sync_channel.cc
index 0ff91d7..5a77819 100644
--- a/ipc/ipc_sync_channel.cc
+++ b/ipc/ipc_sync_channel.cc
@@ -123,39 +123,6 @@
     context->DispatchMessages();
   }
 
-  // Dispatches any queued incoming sync messages. If |dispatching_context| is
-  // not null, messages which target a restricted dispatch channel will only be
-  // dispatched if |dispatching_context| belongs to the same restricted dispatch
-  // group as that channel. If |dispatching_context| is null, all queued
-  // messages are dispatched.
-  void DispatchMessages(SyncContext* dispatching_context) {
-    bool first_time = true;
-    uint32_t expected_version = 0;
-    SyncMessageQueue::iterator it;
-    while (true) {
-      std::unique_ptr<Message> message;
-      scoped_refptr<SyncChannel::SyncContext> context;
-      {
-        base::AutoLock auto_lock(message_lock_);
-        if (first_time || message_queue_version_ != expected_version) {
-          it = message_queue_.begin();
-          first_time = false;
-        }
-        if (it != message_queue_.end()) {
-          message = std::move(it->message);
-          context = std::move(it->context);
-          it = message_queue_.erase(it);
-          message_queue_version_++;
-          expected_version = message_queue_version_;
-        }
-      }
-      if (!message) {
-        break;
-      }
-      context->OnDispatchMessage(*message);
-    }
-  }
-
   // SyncChannel calls this in its destructor.
   void RemoveContext(SyncContext* context) {
     base::AutoLock auto_lock(message_lock_);
@@ -219,13 +186,6 @@
       *dispatch_flag_ = true;
       return;
     }
-
-    // We were woken up during a sync wait, but no specific SyncChannel is
-    // currently waiting. i.e., some other Mojo interface on this thread is
-    // waiting for a response. Since we don't support anything analogous to
-    // restricted dispatch on Mojo interfaces, in this case it's safe to
-    // dispatch sync messages for any context.
-    DispatchMessages(nullptr);
   }
 
   // Holds information about a queued synchronous message or reply.
@@ -336,9 +296,7 @@
   return received_sync_msgs_->dispatch_event();
 }
 
-void SyncChannel::SyncContext::DispatchMessages() {
-  received_sync_msgs_->DispatchMessages(this);
-}
+void SyncChannel::SyncContext::DispatchMessages() {}
 
 bool SyncChannel::SyncContext::TryToUnblockListener(const Message* msg) {
   base::AutoLock auto_lock(deserializers_lock_);
@@ -372,27 +330,6 @@
   Context::Clear();
 }
 
-bool SyncChannel::SyncContext::OnMessageReceived(const Message& msg) {
-  // Give the filters a chance at processing this message.
-  if (TryFilters(msg))
-    return true;
-
-  if (TryToUnblockListener(&msg))
-    return true;
-
-  if (msg.is_reply()) {
-    received_sync_msgs_->QueueReply(msg, this);
-    return true;
-  }
-
-  if (msg.should_unblock()) {
-    received_sync_msgs_->QueueMessage(msg, this);
-    return true;
-  }
-
-  return Context::OnMessageReceivedNoFilter(msg);
-}
-
 void SyncChannel::SyncContext::OnChannelError() {
   CancelPendingSends();
   shutdown_watcher_.StopWatching();
@@ -486,36 +423,6 @@
 
 SyncChannel::~SyncChannel() = default;
 
-bool SyncChannel::Send(Message* message) {
-  TRACE_IPC_MESSAGE_SEND("ipc", "SyncChannel::Send", message);
-  if (!message->is_sync()) {
-    ChannelProxy::SendInternal(message);
-    return true;
-  }
-
-  SyncMessage* sync_msg = static_cast<SyncMessage*>(message);
-
-  // *this* might get deleted in WaitForReply.
-  scoped_refptr<SyncContext> context(sync_context());
-  if (!context->Push(sync_msg)) {
-    DVLOG(1) << "Channel is shutting down. Dropping sync message.";
-    delete message;
-    return false;
-  }
-
-  ChannelProxy::SendInternal(message);
-
-  // Wait for reply, or for any other incoming synchronous messages.
-  // |this| might get deleted, so only call static functions at this point.
-  scoped_refptr<mojo::SyncHandleRegistry> registry = sync_handle_registry_;
-  WaitForReply(registry.get(), context.get());
-
-  TRACE_EVENT_WITH_FLOW0("toplevel.flow", "SyncChannel::Send",
-                         context->GetSendDoneEvent(), TRACE_EVENT_FLAG_FLOW_IN);
-
-  return context->Pop();
-}
-
 void SyncChannel::WaitForReply(mojo::SyncHandleRegistry* registry,
                                SyncContext* context) {
   context->DispatchMessages();
diff --git a/ipc/ipc_sync_channel.h b/ipc/ipc_sync_channel.h
index bf1abafb..4e36da44 100644
--- a/ipc/ipc_sync_channel.h
+++ b/ipc/ipc_sync_channel.h
@@ -103,9 +103,6 @@
 
   ~SyncChannel() override;
 
-  bool Send(Message* message) override;
-
-
  protected:
   friend class ReceivedSyncMsgQueue;
 
@@ -160,7 +157,6 @@
     void Clear() override;
 
     // Called on the IPC thread.
-    bool OnMessageReceived(const Message& msg) override;
     void OnChannelError() override;
     void OnChannelOpened() override;
     void OnChannelClosed() override;
diff --git a/ipc/ipc_sync_channel_unittest.cc b/ipc/ipc_sync_channel_unittest.cc
index cfd15ff..d2c21b3 100644
--- a/ipc/ipc_sync_channel_unittest.cc
+++ b/ipc/ipc_sync_channel_unittest.cc
@@ -85,7 +85,6 @@
     // Shutdown() must be called before destruction.
     CHECK(is_shutdown_);
   }
-  bool Send(Message* msg) override { return channel_->Send(msg); }
   void WaitForChannelCreation() { channel_created_->Wait(); }
   void CloseChannel() {
     DCHECK(ListenerThread()->task_runner()->BelongsToCurrentThread());
@@ -114,22 +113,6 @@
     DCHECK(!overrided_thread_);
     overrided_thread_ = overrided_thread;
   }
-  bool SendAnswerToLife(bool succeed) {
-    int answer = 0;
-    SyncMessage* msg = new SyncChannelTestMsg_AnswerToLife(&answer);
-    bool result = Send(msg);
-    DCHECK_EQ(result, succeed);
-    DCHECK_EQ(answer, (succeed ? 42 : 0));
-    return result;
-  }
-  bool SendDouble(bool succeed) {
-    int answer = 0;
-    SyncMessage* msg = new SyncChannelTestMsg_Double(5, &answer);
-    bool result = Send(msg);
-    DCHECK_EQ(result, succeed);
-    DCHECK_EQ(answer, (succeed ? 10 : 0));
-    return result;
-  }
   mojo::MessagePipeHandle TakeChannelHandle() {
     DCHECK(channel_handle_.is_valid());
     return channel_handle_.release();
@@ -146,27 +129,6 @@
   SyncChannel* channel() { return channel_.get(); }
   // Functions for derived classes to implement if they wish.
   virtual void Run() { }
-  virtual void OnAnswer(int* answer) { NOTREACHED(); }
-  virtual void OnAnswerDelay(Message* reply_msg) {
-    // The message handler map below can only take one entry for
-    // SyncChannelTestMsg_AnswerToLife, so since some classes want
-    // the normal version while other want the delayed reply, we
-    // call the normal version if the derived class didn't override
-    // this function.
-    int answer;
-    OnAnswer(&answer);
-    SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, answer);
-    Send(reply_msg);
-  }
-  virtual void OnDouble(int in, int* out) { NOTREACHED(); }
-  virtual void OnDoubleDelay(int in, Message* reply_msg) {
-    int result;
-    OnDouble(in, &result);
-    SyncChannelTestMsg_Double::WriteReplyParams(reply_msg, result);
-    Send(reply_msg);
-  }
-
-  virtual void OnNestedTestMsg(Message* reply_msg) { NOTREACHED(); }
 
   virtual SyncChannel* CreateChannel() {
     std::unique_ptr<SyncChannel> channel = SyncChannel::Create(
@@ -224,17 +186,6 @@
     listener_event->Signal();
   }
 
-  bool OnMessageReceived(const Message& message) override {
-    IPC_BEGIN_MESSAGE_MAP(Worker, message)
-     IPC_MESSAGE_HANDLER_DELAY_REPLY(SyncChannelTestMsg_Double, OnDoubleDelay)
-     IPC_MESSAGE_HANDLER_DELAY_REPLY(SyncChannelTestMsg_AnswerToLife,
-                                     OnAnswerDelay)
-     IPC_MESSAGE_HANDLER_DELAY_REPLY(SyncChannelNestedTestMsg_String,
-                                     OnNestedTestMsg)
-    IPC_END_MESSAGE_MAP()
-    return true;
-  }
-
   void StartThread(base::Thread* thread, base::MessagePumpType type) {
     base::Thread::Options options;
     options.message_pump_type = type;
@@ -277,14 +228,6 @@
                std::move(channel_handle)),
         send_result_(true) {}
 
-  bool SendDummy() {
-    ListenerThread()->task_runner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(base::IgnoreResult(&ServerSendAfterClose::Send),
-                       base::Unretained(this), new SyncChannelTestMsg_NoArgs));
-    return true;
-  }
-
   bool send_result() const {
     return send_result_;
   }
@@ -295,12 +238,6 @@
     Done();
   }
 
-  bool Send(Message* msg) override {
-    send_result_ = Worker::Send(msg);
-    Done();
-    return send_result_;
-  }
-
   bool send_result_;
 };
 
@@ -312,12 +249,6 @@
 
   server.done_event()->Wait();
   server.done_event()->Reset();
-
-  server.SendDummy();
-  server.done_event()->Wait();
-
-  EXPECT_FALSE(server.send_result());
-
   server.Shutdown();
 }
 
diff --git a/media/audio/apple/audio_input.cc b/media/audio/apple/audio_input.cc
index 80e507a..9a2eb4d 100644
--- a/media/audio/apple/audio_input.cc
+++ b/media/audio/apple/audio_input.cc
@@ -328,11 +328,11 @@
 }
 
 void PCMQueueInAudioInputStream::SetInputCallbackIsActive(bool enabled) {
-  base::subtle::Release_Store(&input_callback_is_active_, enabled);
+  input_callback_is_active_.store(enabled, std::memory_order_release);
 }
 
 bool PCMQueueInAudioInputStream::GetInputCallbackIsActive() {
-  return (base::subtle::Acquire_Load(&input_callback_is_active_) != false);
+  return input_callback_is_active_.load(std::memory_order_acquire);
 }
 
 void PCMQueueInAudioInputStream::CheckInputStartupSuccess() {
diff --git a/media/audio/apple/audio_input.h b/media/audio/apple/audio_input.h
index 34757cf6..dfee5226 100644
--- a/media/audio/apple/audio_input.h
+++ b/media/audio/apple/audio_input.h
@@ -9,9 +9,9 @@
 #include <AudioToolbox/AudioQueue.h>
 #include <stdint.h>
 
+#include <atomic>
 #include <memory>
 
-#include "base/atomicops.h"
 #include "base/cancelable_callback.h"
 #include "base/compiler_specific.h"
 #include "base/memory/raw_ptr.h"
@@ -108,7 +108,7 @@
 
   // Is set to true on the internal AUHAL IO thread in the first input callback
   // after Start() has bee called.
-  base::subtle::Atomic32 input_callback_is_active_;
+  std::atomic<bool> input_callback_is_active_;
 
   // Timer which triggers CheckInputStartupSuccess() to verify that input
   // callbacks have started as intended after a successful call to Start().
diff --git a/media/audio/apple/audio_low_latency_input.cc b/media/audio/apple/audio_low_latency_input.cc
index 5eda5e5..6cde211 100644
--- a/media/audio/apple/audio_low_latency_input.cc
+++ b/media/audio/apple/audio_low_latency_input.cc
@@ -1179,11 +1179,11 @@
 }
 
 void AUAudioInputStream::SetInputCallbackIsActive(bool enabled) {
-  base::subtle::Release_Store(&input_callback_is_active_, enabled);
+  input_callback_is_active_.store(enabled, std::memory_order_release);
 }
 
 bool AUAudioInputStream::GetInputCallbackIsActive() {
-  return (base::subtle::Acquire_Load(&input_callback_is_active_) != false);
+  return input_callback_is_active_.load(std::memory_order_acquire);
 }
 
 void AUAudioInputStream::CheckInputStartupSuccess() {
diff --git a/media/audio/apple/audio_low_latency_input.h b/media/audio/apple/audio_low_latency_input.h
index 96493f2..3d4e8ea 100644
--- a/media/audio/apple/audio_low_latency_input.h
+++ b/media/audio/apple/audio_low_latency_input.h
@@ -37,10 +37,10 @@
 
 #include <AudioUnit/AudioUnit.h>
 
+#include <atomic>
 #include <memory>
 #include <vector>
 
-#include "base/atomicops.h"
 #include "base/cancelable_callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/threading/thread_checker.h"
@@ -224,7 +224,7 @@
   // is safe since after stopping the audio unit there is no current callback
   // ongoing and no further callbacks coming.
   bool got_input_callback_ = false;
-  base::subtle::Atomic32 input_callback_is_active_ = false;
+  std::atomic<bool> input_callback_is_active_ = false;
 
   // Timer which triggers CheckInputStartupSuccess() to verify that input
   // callbacks have started as intended after a successful call to Start().
diff --git a/media/base/audio_bus.cc b/media/base/audio_bus.cc
index 7b384f5..68db859 100644
--- a/media/base/audio_bus.cc
+++ b/media/base/audio_bus.cc
@@ -105,17 +105,6 @@
   BuildChannelData(channels, data);
 }
 
-AudioBus::AudioBus(int frames, const std::vector<float*>& channel_data)
-    : frames_(base::checked_cast<size_t>(frames)) {
-  CHECK(IsValidChannelCount(channel_data.size()));
-  channel_data_.reserve(channel_data.size());
-
-  for (float* data : channel_data) {
-    CHECK(IsAligned(data));
-    channel_data_.emplace_back(data, frames_);
-  }
-}
-
 AudioBus::AudioBus(int channels) : channel_data_(channels), is_wrapper_(true) {
   CHECK(IsValidChannelCount(channels));
 }
@@ -139,12 +128,6 @@
   return base::WrapUnique(new AudioBus(channels));
 }
 
-std::unique_ptr<AudioBus> AudioBus::WrapVector(
-    int frames,
-    const std::vector<float*>& channel_data) {
-  return base::WrapUnique(new AudioBus(frames, channel_data));
-}
-
 std::unique_ptr<AudioBus> AudioBus::WrapMemory(int channels,
                                                int frames,
                                                void* data) {
diff --git a/media/base/audio_bus.h b/media/base/audio_bus.h
index 34b0ebd..cafef88 100644
--- a/media/base/audio_bus.h
+++ b/media/base/audio_bus.h
@@ -51,13 +51,6 @@
   // to wrap externally allocated memory.
   static std::unique_ptr<AudioBus> CreateWrapper(int channels);
 
-  // Creates a new AudioBus from an existing channel vector.  Does not transfer
-  // ownership of |channel_data| to AudioBus; i.e., |channel_data| must outlive
-  // the returned AudioBus.  Each channel must be aligned by kChannelAlignment.
-  static std::unique_ptr<AudioBus> WrapVector(
-      int frames,
-      const std::vector<float*>& channel_data);
-
   // Creates a new AudioBus by wrapping an existing block of memory.  Block must
   // be at least CalculateMemorySize() bytes in size.  |data| must outlive the
   // returned AudioBus.  |data| must be aligned by kChannelAlignment.
@@ -239,7 +232,6 @@
   AudioBus(int channels, int frames);
   AudioBus(int channels, int frames, float* data);
   AudioBus(int channels, int frames, base::span<float> data);
-  AudioBus(int frames, const std::vector<float*>& channel_data);
   explicit AudioBus(int channels);
 
  private:
diff --git a/media/base/audio_bus_unittest.cc b/media/base/audio_bus_unittest.cc
index 500f9e6..2c21f5d1 100644
--- a/media/base/audio_bus_unittest.cc
+++ b/media/base/audio_bus_unittest.cc
@@ -261,23 +261,6 @@
   EXPECT_EQ(current_channel, kChannels);
 }
 
-// Verify an AudioBus created via wrapping a vector works as advertised.
-TEST_F(AudioBusTest, WrapVector) {
-  AllocateDataPerChannel();
-
-  std::vector<float*> data_pointers;
-  data_pointers.reserve(kChannels);
-
-  for (AlignedFloatArray& data : data_) {
-    data_pointers.push_back(data.as_span().data());
-  }
-
-  std::unique_ptr<AudioBus> bus =
-      AudioBus::WrapVector(kFrameCount, GetRawPointers(data_));
-  VerifyChannelAndFrameCount(bus.get());
-  VerifyReadWriteAndAlignment(bus.get());
-}
-
 // Verify an AudioBus created via wrapping a memory block works as advertised.
 TEST_F(AudioBusTest, WrapMemory) {
   AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR,
@@ -326,14 +309,6 @@
     CopyTest(bus1.get(), bus2.get());
   }
   {
-    SCOPED_TRACE("Wrapped Vector");
-    // Try a copy to an AudioBus wrapping a vector.
-    AllocateDataPerChannel();
-
-    bus2 = AudioBus::WrapVector(kFrameCount, GetRawPointers(data_));
-    CopyTest(bus1.get(), bus2.get());
-  }
-  {
     SCOPED_TRACE("Wrapped Memory");
     // Try a copy to an AudioBus wrapping a memory block.
     std::unique_ptr<float, base::AlignedFreeDeleter> data(static_cast<float*>(
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index c10c81e..35ad3f0 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -1229,6 +1229,9 @@
              base::FEATURE_DISABLED_BY_DEFAULT
 #endif
 );
+
+// Use shared image interface to transport video frame resources.
+BASE_FEATURE(kUseSharedImageInOOPVDProcess, base::FEATURE_DISABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 8d3a9df..2f3aba2e6 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -493,6 +493,7 @@
 // is enabled instead of directly checking this feature flag. The reason is that
 // that function may perform checks beyond the feature flag.
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kUseOutOfProcessVideoDecoding);
+MEDIA_EXPORT BASE_DECLARE_FEATURE(kUseSharedImageInOOPVDProcess);
 #endif  // BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
diff --git a/media/base/video_bitrate_allocation.cc b/media/base/video_bitrate_allocation.cc
index 25d98375..2d66817 100644
--- a/media/base/video_bitrate_allocation.cc
+++ b/media/base/video_bitrate_allocation.cc
@@ -11,7 +11,6 @@
 #include <sstream>
 
 #include "base/check_op.h"
-#include "base/compiler_specific.h"
 #include "base/numerics/checked_math.h"
 #include "media/base/bitrate.h"
 
@@ -169,12 +168,7 @@
 
 bool VideoBitrateAllocation::operator==(
     const VideoBitrateAllocation& other) const {
-  if (sum_bitrate_ != other.sum_bitrate_) {
-    return false;
-  }
-  return UNSAFE_TODO(memcmp(bitrates_.data(), other.bitrates_.data(),
-                            (bitrates_.size() *
-                             sizeof(decltype(bitrates_)::value_type)))) == 0;
+  return sum_bitrate_ == other.sum_bitrate_ && bitrates_ == other.bitrates_;
 }
 
 }  // namespace media
diff --git a/media/base/video_codec_string_parsers.cc b/media/base/video_codec_string_parsers.cc
index 7ba439b..935cf1c 100644
--- a/media/base/video_codec_string_parsers.cc
+++ b/media/base/video_codec_string_parsers.cc
@@ -7,7 +7,6 @@
 #include <array>
 #include <string_view>
 
-#include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/notreached.h"
 #include "base/strings/string_number_conversions.h"
@@ -674,10 +673,7 @@
     return std::nullopt;
   }
 
-  std::array<uint8_t, 6> constraint_flags;
-  UNSAFE_TODO(memset(constraint_flags.data(), 0,
-                     (constraint_flags.size() *
-                      sizeof(decltype(constraint_flags)::value_type))));
+  std::array<uint8_t, 6> constraint_flags = {};
 
   if (elem.size() > 10) {
     DVLOG(4) << __func__ << ": unexpected number of trailing bytes in HEVC "
diff --git a/media/capture/video/win/BUILD.gn b/media/capture/video/win/BUILD.gn
index 5b513af6..33905f04 100644
--- a/media/capture/video/win/BUILD.gn
+++ b/media/capture/video/win/BUILD.gn
@@ -44,6 +44,7 @@
   deps = [
     "//base",
     "//base:i18n",
+    "//components/viz/common/resources:shared_image_format",
     "//media",
     "//media/base/win:color_space_util_win",
     "//media/capture/mojom:image_capture",
diff --git a/media/capture/video/win/gpu_memory_buffer_tracker_win.cc b/media/capture/video/win/gpu_memory_buffer_tracker_win.cc
index 206c254..ba1ef717 100644
--- a/media/capture/video/win/gpu_memory_buffer_tracker_win.cc
+++ b/media/capture/video/win/gpu_memory_buffer_tracker_win.cc
@@ -14,6 +14,7 @@
 #include "base/trace_event/trace_event.h"
 #include "base/unguessable_token.h"
 #include "base/win/scoped_handle.h"
+#include "components/viz/common/resources/shared_image_format_utils.h"
 #include "gpu/ipc/common/dxgi_helpers.h"
 #include "media/base/win/mf_helpers.h"
 #include "media/capture/video/video_capture_buffer_handle.h"
@@ -160,9 +161,9 @@
 
   dxgi_handle_ = std::move(buffer_handle).dxgi_handle();
   dimensions_ = dimensions;
-  stride_ = gfx::RowSizeForBufferFormat(dimensions_.width(),
-                                        gfx::BufferFormat::YUV_420_BIPLANAR,
-                                        /*plane=*/0);
+  stride_ = viz::SharedMemoryRowSizeForSharedImageFormat(
+                viz::MultiPlaneFormat::kNV12, /*plane=*/0, dimensions_.width())
+                .value();
 
   region_ = base::UnsafeSharedMemoryRegion::Create(GetMemorySizeInBytes());
   mapping_ = region_.Map();
diff --git a/media/filters/audio_renderer_algorithm.cc b/media/filters/audio_renderer_algorithm.cc
index 1ec47cc..177c525 100644
--- a/media/filters/audio_renderer_algorithm.cc
+++ b/media/filters/audio_renderer_algorithm.cc
@@ -675,19 +675,28 @@
 void AudioRendererAlgorithm::CreateSearchWrappers() {
   // WSOLA is quite expensive to run, so if a channel mask exists, use it to
   // reduce the size of our search space.
-  std::vector<float*> active_target_channels;
-  std::vector<float*> active_search_channels;
+  AudioBus::ChannelVector active_target_channels;
+  AudioBus::ChannelVector active_search_channels;
   for (int ch = 0; ch < channels_; ++ch) {
     if (channel_mask_[ch]) {
-      active_target_channels.push_back(target_block_->channel_span(ch).data());
-      active_search_channels.push_back(search_block_->channel_span(ch).data());
+      active_target_channels.push_back(target_block_->channel_span(ch));
+      active_search_channels.push_back(search_block_->channel_span(ch));
     }
   }
 
+  auto create_wrapper_bus = [](const AudioBus::ChannelVector& channels,
+                               int frames) {
+    auto bus = AudioBus::CreateWrapper(channels.size());
+    bus->set_frames(frames);
+    bus->SetAllChannels(channels);
+    return bus;
+  };
+
   target_block_wrapper_ =
-      AudioBus::WrapVector(target_block_->frames(), active_target_channels);
+      create_wrapper_bus(active_target_channels, target_block_->frames());
+
   search_block_wrapper_ =
-      AudioBus::WrapVector(search_block_->frames(), active_search_channels);
+      create_wrapper_bus(active_search_channels, search_block_->frames());
 }
 
 void AudioRendererAlgorithm::SetPreservesPitch(bool preserves_pitch) {
diff --git a/media/gpu/chromeos/BUILD.gn b/media/gpu/chromeos/BUILD.gn
index f854ada2..509d8b1 100644
--- a/media/gpu/chromeos/BUILD.gn
+++ b/media/gpu/chromeos/BUILD.gn
@@ -91,6 +91,8 @@
     "registered_frame_converter.cc",
     "registered_frame_converter.h",
     "shaders/shaders.h",
+    "simple_video_frame_converter.cc",
+    "simple_video_frame_converter.h",
     "vd_video_decode_accelerator.cc",
     "vd_video_decode_accelerator.h",
     "vda_video_frame_pool.cc",
diff --git a/media/gpu/chromeos/dmabuf_video_frame_converter.h b/media/gpu/chromeos/dmabuf_video_frame_converter.h
index dafed7d6..44a92ca 100644
--- a/media/gpu/chromeos/dmabuf_video_frame_converter.h
+++ b/media/gpu/chromeos/dmabuf_video_frame_converter.h
@@ -12,8 +12,10 @@
 namespace media {
 
 // DmabufVideoFrameConverter can be used to convert a NativePixmapFrameResource
-// to a STORAGE_DMABUFS VideoFrame. It is used by the decoder utility process to
-// transport DMA buffer-backed VideoFrames to the OOPVideoDecoder.
+// to a STORAGE_DMABUFS VideoFrame.
+// TODO(https://crbug.com/403183890): This converter and the corresponding
+// dmabuf dup code in video_frame_mojom_traits.cc can be removed if we never
+// send dmabuf directly in VideoFrames via mojo.
 class MEDIA_GPU_EXPORT DmabufVideoFrameConverter
     : public FrameResourceConverter {
  public:
diff --git a/media/gpu/chromeos/oop_video_decoder.cc b/media/gpu/chromeos/oop_video_decoder.cc
index 6e515e0..141239b4 100644
--- a/media/gpu/chromeos/oop_video_decoder.cc
+++ b/media/gpu/chromeos/oop_video_decoder.cc
@@ -12,6 +12,7 @@
 #include "build/build_config.h"
 #include "chromeos/components/cdm_factory_daemon/cdm_context_for_oopvd_impl.h"
 #include "media/base/format_utils.h"
+#include "media/base/media_switches.h"
 #include "media/base/video_util.h"
 #include "media/gpu/buffer_validation.h"
 #include "media/gpu/chromeos/native_pixmap_frame_resource.h"
@@ -976,38 +977,65 @@
     return;
   }
 
-  if (frame->storage_type() != VideoFrame::STORAGE_DMABUFS) {
-    VLOGF(2) << "Received a frame with an unexpected storage type";
-    Stop();
-    return;
+  static const bool extract_shared_image =
+      base::FeatureList::IsEnabled(kUseSharedImageInOOPVDProcess);
+  std::vector<base::ScopedFD> duped_fds;
+
+  if (extract_shared_image) {
+    if (frame->storage_type() != VideoFrame::STORAGE_OPAQUE) {
+      VLOGF(2) << "Received a frame with an unexpected storage type";
+      Stop();
+      return;
+    }
+
+    if (!frame->HasSharedImage()) {
+      VLOGF(2) << "Received a frame without shared image";
+      Stop();
+      return;
+    }
+
+    const size_t num_fds = frame->NumDmabufFds();
+    if (num_fds > 0) {
+      VLOGF(2) << "Received a frame with DMA buffer FD's";
+      Stop();
+      return;
+    }
+  } else {
+    if (frame->storage_type() != VideoFrame::STORAGE_DMABUFS) {
+      VLOGF(2) << "Received a frame with an unexpected storage type";
+      Stop();
+      return;
+    }
+
+    // The mojo traits guarantee this.
+    CHECK(gfx::Rect(frame->coded_size()).Contains(frame->visible_rect()));
+
+    const size_t num_fds = frame->NumDmabufFds();
+    if (0 == num_fds) {
+      VLOGF(2) << "Received a frame with zero DMA buffer FD's";
+      Stop();
+      return;
+    }
+
+    duped_fds.reserve(num_fds);
+    for (size_t i = 0; i < num_fds; ++i) {
+      if (frame->GetDmabufFd(i) < 0) {
+        VLOGF(2) << "Received at least one invalid FD";
+        Stop();
+        return;
+      }
+      duped_fds.emplace_back(HANDLE_EINTR(dup(frame->GetDmabufFd(i))));
+      if (!duped_fds.back().is_valid()) {
+        VLOGF(2) << "Failed to dup() an FD";
+        Stop();
+        return;
+      }
+    }
   }
 
   // The mojo traits guarantee this.
   CHECK(gfx::Rect(frame->coded_size()).Contains(frame->visible_rect()));
 
-  const size_t num_fds = frame->NumDmabufFds();
-  if (0 == num_fds) {
-    VLOGF(2) << "Received a frame with zero DMA buffer FD's";
-    Stop();
-    return;
-  }
-
-  std::vector<base::ScopedFD> duped_fds;
-  duped_fds.reserve(num_fds);
-  for (size_t i = 0; i < num_fds; ++i) {
-    if (frame->GetDmabufFd(i) < 0) {
-      VLOGF(2) << "Received at least one invalid FD";
-      Stop();
-      return;
-    }
-    duped_fds.emplace_back(HANDLE_EINTR(dup(frame->GetDmabufFd(i))));
-    if (!duped_fds.back().is_valid()) {
-      VLOGF(2) << "Failed to dup() an FD";
-      Stop();
-      return;
-    }
-  }
-
   const base::TimeDelta fake_timestamp = frame->timestamp();
   auto it = fake_timestamp_to_real_timestamp_cache_.Get(fake_timestamp);
   if (it == fake_timestamp_to_real_timestamp_cache_.end()) {
@@ -1043,110 +1071,119 @@
     return;
   }
 
-  // What follows is all the logic necessary to recycle buffers safely.
-  //
-  // Note that the way we track buffers is with the frame's tracking token. In
-  // theory, this should uniquely identify allocated buffers. In practice, we
-  // can't trust what comes from the remote decoder. A malicious decoder could
-  // send us two frames that have the same tracking token, but actually refer to
-  // different dma-bufs. The solution to this is that we assume that the remote
-  // decoder is telling the truth in a sense: if we receive an incoming buffer
-  // with a tracking_token that we already know about (by looking it up in
-  // |received_token_to_decoded_frame_map_|), we will re-use the buffer that we
-  // know about and ignore the incoming one. The goal with the rest of the logic
-  // below is that if this assumption is violated, the worst case is a visually
-  // incorrect output but not a security problem.
-  //
-  // When something like a resolution change happens, we assume that the remote
-  // decoder recreated its pool of buffers. Therefore, in those cases we can
-  // forget about all known frames since we shouldn't see those buffers again.
-  // In order to detect those cases, we replicate the logic from
-  // PlatformVideoFramePool::IsSameFormat_Locked().
-  const VideoPixelFormat format = frame->format();
-  const gfx::Size coded_size = frame->coded_size();
-  const gfx::Rect visible_rect = frame->visible_rect();
-  const gfx::Size natural_size = frame->natural_size();
-  const gfx::ColorSpace color_space = frame->ColorSpace();
-  const std::optional<gfx::HDRMetadata> hdr_metadata = frame->hdr_metadata();
-  const VideoFrameMetadata metadata = metadata_to_propagate;
-  const base::UnguessableToken received_tracking_token =
-      *frame->metadata().tracking_token;
-  if (!received_token_to_decoded_frame_map_.empty()) {
-    // It doesn't matter which frame we pick to calculate the current state. All
-    // of them should yield the same result.
-    const VideoPixelFormat current_format =
-        received_token_to_decoded_frame_map_.cbegin()->second->format();
-    const gfx::Size& current_coded_size =
-        received_token_to_decoded_frame_map_.cbegin()->second->coded_size();
-    const gfx::Size& current_visible_rect_size_from_origin =
-        GetRectSizeFromOrigin(received_token_to_decoded_frame_map_.cbegin()
-                                  ->second->visible_rect());
-    const bool currently_uses_protected =
-        received_token_to_decoded_frame_map_.cbegin()
-            ->second->metadata()
-            .hw_protected;
-
-    if (format != current_format || coded_size != current_coded_size ||
-        GetRectSizeFromOrigin(visible_rect) !=
-            current_visible_rect_size_from_origin ||
-        metadata.hw_protected != currently_uses_protected) {
-      received_token_to_decoded_frame_map_.clear();
-      generated_token_to_decoded_frame_map_.clear();
-    }
-  }
-
-  scoped_refptr<FrameResource> frame_to_wrap;
-  auto decoded_frame_it =
-      received_token_to_decoded_frame_map_.find(received_tracking_token);
-  if (decoded_frame_it != received_token_to_decoded_frame_map_.end()) {
-    frame_to_wrap = decoded_frame_it->second;
-    CHECK_EQ(frame_to_wrap->format(), format);
-    CHECK_EQ(frame_to_wrap->coded_size(), coded_size);
-    CHECK_EQ(GetRectSizeFromOrigin(frame_to_wrap->visible_rect()),
-             GetRectSizeFromOrigin(visible_rect));
-    CHECK_EQ(frame_to_wrap->metadata().hw_protected, metadata.hw_protected);
+  scoped_refptr<FrameResource> wrapped_frame;
+  if (extract_shared_image) {
+    frame->set_timestamp(real_timestamp);
+    frame->AddDestructionObserver(base::BindPostTaskToCurrentDefault(
+        base::BindOnce(&OOPVideoDecoder::ReleaseVideoFrame,
+                       weak_this_factory_.GetWeakPtr(), *release_token)));
+    wrapped_frame = VideoFrameResource::Create(std::move(frame));
   } else {
-    scoped_refptr<FrameResource> native_pixmap_frame =
-        CreateDecodedFrameResource(frame->layout(), visible_rect, natural_size,
-                                   std::move(duped_fds), fake_timestamp,
-                                   metadata, color_space, hdr_metadata);
-    if (!native_pixmap_frame) {
+    // What follows is all the logic necessary to recycle buffers safely.
+    //
+    // Note that the way we track buffers is with the frame's tracking token. In
+    // theory, this should uniquely identify allocated buffers. In practice, we
+    // can't trust what comes from the remote decoder. A malicious decoder could
+    // send us two frames that have the same tracking token, but actually refer
+    // to different dma-bufs. The solution to this is that we assume that the
+    // remote decoder is telling the truth in a sense: if we receive an incoming
+    // buffer with a tracking_token that we already know about (by looking it up
+    // in |received_token_to_decoded_frame_map_|), we will reuse the buffer that
+    // we know about and ignore the incoming one. The goal with the rest of the
+    // logic below is that if this assumption is violated, the worst case is a
+    // visually incorrect output but not a security problem.
+    //
+    // When something like a resolution change happens, we assume that the
+    // remote decoder recreated its pool of buffers. Therefore, in those cases
+    // we can forget about all known frames since we shouldn't see those buffers
+    // again. In order to detect those cases, we replicate the logic from
+    // PlatformVideoFramePool::IsSameFormat_Locked().
+    const VideoPixelFormat format = frame->format();
+    const gfx::Size coded_size = frame->coded_size();
+    const gfx::Rect visible_rect = frame->visible_rect();
+    const gfx::Size natural_size = frame->natural_size();
+    const gfx::ColorSpace color_space = frame->ColorSpace();
+    const std::optional<gfx::HDRMetadata> hdr_metadata = frame->hdr_metadata();
+    const VideoFrameMetadata metadata = metadata_to_propagate;
+    const base::UnguessableToken received_tracking_token =
+        *frame->metadata().tracking_token;
+    if (!received_token_to_decoded_frame_map_.empty()) {
+      // It doesn't matter which frame we pick to calculate the current state.
+      // All of them should yield the same result.
+      const VideoPixelFormat current_format =
+          received_token_to_decoded_frame_map_.cbegin()->second->format();
+      const gfx::Size& current_coded_size =
+          received_token_to_decoded_frame_map_.cbegin()->second->coded_size();
+      const gfx::Size& current_visible_rect_size_from_origin =
+          GetRectSizeFromOrigin(received_token_to_decoded_frame_map_.cbegin()
+                                    ->second->visible_rect());
+      const bool currently_uses_protected =
+          received_token_to_decoded_frame_map_.cbegin()
+              ->second->metadata()
+              .hw_protected;
+
+      if (format != current_format || coded_size != current_coded_size ||
+          GetRectSizeFromOrigin(visible_rect) !=
+              current_visible_rect_size_from_origin ||
+          metadata.hw_protected != currently_uses_protected) {
+        received_token_to_decoded_frame_map_.clear();
+        generated_token_to_decoded_frame_map_.clear();
+      }
+    }
+
+    scoped_refptr<FrameResource> frame_to_wrap;
+    auto decoded_frame_it =
+        received_token_to_decoded_frame_map_.find(received_tracking_token);
+    if (decoded_frame_it != received_token_to_decoded_frame_map_.end()) {
+      frame_to_wrap = decoded_frame_it->second;
+      CHECK_EQ(frame_to_wrap->format(), format);
+      CHECK_EQ(frame_to_wrap->coded_size(), coded_size);
+      CHECK_EQ(GetRectSizeFromOrigin(frame_to_wrap->visible_rect()),
+               GetRectSizeFromOrigin(visible_rect));
+      CHECK_EQ(frame_to_wrap->metadata().hw_protected, metadata.hw_protected);
+    } else {
+      scoped_refptr<FrameResource> native_pixmap_frame =
+          CreateDecodedFrameResource(
+              frame->layout(), visible_rect, natural_size, std::move(duped_fds),
+              fake_timestamp, metadata, color_space, hdr_metadata);
+      if (!native_pixmap_frame) {
+        Stop();
+        return;
+      }
+      received_token_to_decoded_frame_map_[received_tracking_token] =
+          native_pixmap_frame;
+      generated_token_to_decoded_frame_map_[native_pixmap_frame
+                                                ->tracking_token()] =
+          native_pixmap_frame.get();
+      frame_to_wrap = std::move(native_pixmap_frame);
+    }
+
+    // If |frame_to_wrap| was cached in |received_token_to_decoded_frame_map_|,
+    // then there is a possibility that |visible_rect| and |natural_size|, which
+    // are computed from |frame| are different than in |frame_to_wrap| and
+    // |frame|. Because of this, CreateWrappingFrame() is called with
+    // |visible_rect| and |natural_size|.
+    wrapped_frame =
+        frame_to_wrap->CreateWrappingFrame(visible_rect, natural_size);
+    if (!wrapped_frame) {
+      VLOGF(2) << "Could not wrap the frame";
       Stop();
       return;
     }
-    received_token_to_decoded_frame_map_[received_tracking_token] =
-        native_pixmap_frame;
-    generated_token_to_decoded_frame_map_[native_pixmap_frame
-                                              ->tracking_token()] =
-        native_pixmap_frame.get();
-    frame_to_wrap = std::move(native_pixmap_frame);
+
+    wrapped_frame->set_timestamp(real_timestamp);
+    wrapped_frame->set_color_space(color_space);
+    wrapped_frame->set_hdr_metadata(hdr_metadata);
+    wrapped_frame->set_metadata(metadata);
+
+    // The destruction observer will be called after the client releases the
+    // video frame. base::BindPostTaskToCurrentDefault() is used to make sure
+    // that the WeakPtr is dereferenced on the correct sequence.
+    wrapped_frame->AddDestructionObserver(base::BindPostTaskToCurrentDefault(
+        base::BindOnce(&OOPVideoDecoder::ReleaseVideoFrame,
+                       weak_this_factory_.GetWeakPtr(), *release_token)));
   }
 
-  // If |frame_to_wrap| was cached in |received_token_to_decoded_frame_map_|,
-  // then there is a possibility that |visible_rect| and |natural_size|, which
-  // are computed from |frame| are different than in |frame_to_wrap| and
-  // |frame|. Because of this, CreateWrappingFrame() is called with
-  // |visible_rect| and |natural_size|.
-  scoped_refptr<FrameResource> wrapped_frame =
-      frame_to_wrap->CreateWrappingFrame(visible_rect, natural_size);
-  if (!wrapped_frame) {
-    VLOGF(2) << "Could not wrap the frame";
-    Stop();
-    return;
-  }
-
-  wrapped_frame->set_timestamp(real_timestamp);
-  wrapped_frame->set_color_space(color_space);
-  wrapped_frame->set_hdr_metadata(hdr_metadata);
-  wrapped_frame->set_metadata(metadata);
-
-  // The destruction observer will be called after the client releases the
-  // video frame. base::BindPostTaskToCurrentDefault() is used to make sure that
-  // the WeakPtr is dereferenced on the correct sequence.
-  wrapped_frame->AddDestructionObserver(base::BindPostTaskToCurrentDefault(
-      base::BindOnce(&OOPVideoDecoder::ReleaseVideoFrame,
-                     weak_this_factory_.GetWeakPtr(), *release_token)));
-
   can_read_without_stalling_ = can_read_without_stalling;
 
   if (output_cb_) {
diff --git a/media/gpu/chromeos/simple_video_frame_converter.cc b/media/gpu/chromeos/simple_video_frame_converter.cc
new file mode 100644
index 0000000..e46a6383
--- /dev/null
+++ b/media/gpu/chromeos/simple_video_frame_converter.cc
@@ -0,0 +1,37 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/chromeos/simple_video_frame_converter.h"
+
+#include "base/logging.h"
+#include "media/base/video_frame.h"
+#include "media/gpu/chromeos/video_frame_resource.h"
+#include "media/gpu/macros.h"
+
+namespace media {
+
+// static.
+std::unique_ptr<FrameResourceConverter> SimpleVideoFrameConverter::Create() {
+  return base::WrapUnique<FrameResourceConverter>(
+      new SimpleVideoFrameConverter());
+}
+
+void SimpleVideoFrameConverter::ConvertFrameImpl(
+    scoped_refptr<FrameResource> frame) {
+  DVLOGF(4);
+
+  if (!frame) {
+    return OnError(FROM_HERE, "Invalid frame.");
+  }
+  LOG_ASSERT(frame->AsVideoFrameResource())
+      << "|frame| is expected to be a AsVideoFrameResource";
+  scoped_refptr<VideoFrame> video_frame =
+      frame->AsVideoFrameResource()->GetMutableVideoFrame();
+  if (!video_frame) {
+    return OnError(FROM_HERE, "Failed to convert FrameResource to VideoFrame.");
+  }
+  Output(std::move(video_frame));
+}
+
+}  // namespace media
diff --git a/media/gpu/chromeos/simple_video_frame_converter.h b/media/gpu/chromeos/simple_video_frame_converter.h
new file mode 100644
index 0000000..f05a8a2
--- /dev/null
+++ b/media/gpu/chromeos/simple_video_frame_converter.h
@@ -0,0 +1,34 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_CHROMEOS_SIMPLE_VIDEO_FRAME_CONVERTER_H_
+#define MEDIA_GPU_CHROMEOS_SIMPLE_VIDEO_FRAME_CONVERTER_H_
+
+#include "media/gpu/chromeos/frame_resource_converter.h"
+#include "media/gpu/media_gpu_export.h"
+
+namespace media {
+
+// SimpleVideoFrameConverter is used to retrieve VideoFrame from the
+// VideoFrameResource that is wrapped by OOPVideoDecoder.
+class MEDIA_GPU_EXPORT SimpleVideoFrameConverter
+    : public FrameResourceConverter {
+ public:
+  static std::unique_ptr<FrameResourceConverter> Create();
+
+  SimpleVideoFrameConverter(const SimpleVideoFrameConverter&) = delete;
+  SimpleVideoFrameConverter& operator=(const SimpleVideoFrameConverter&) =
+      delete;
+
+ private:
+  SimpleVideoFrameConverter() = default;
+  ~SimpleVideoFrameConverter() override = default;
+
+  // FrameConverter overrides.
+  void ConvertFrameImpl(scoped_refptr<FrameResource> frame) override;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_CHROMEOS_SIMPLE_VIDEO_FRAME_CONVERTER_H_
diff --git a/media/gpu/chromeos/video_decoder_pipeline.cc b/media/gpu/chromeos/video_decoder_pipeline.cc
index 69bc3bc..2e4c6465 100644
--- a/media/gpu/chromeos/video_decoder_pipeline.cc
+++ b/media/gpu/chromeos/video_decoder_pipeline.cc
@@ -219,16 +219,17 @@
     mojo::PendingRemote<mojom::VideoDecoder> oop_video_decoder,
     bool in_video_decoder_process) {
   DCHECK(client_task_runner);
-  DCHECK(frame_pool);
   DCHECK(!renderable_fourccs.empty());
 
   CreateDecoderFunctionCB create_decoder_function_cb;
   bool uses_oop_video_decoder = false;
   if (oop_video_decoder) {
+    DCHECK(!frame_pool);
     create_decoder_function_cb =
         base::BindOnce(&OOPVideoDecoder::Create, std::move(oop_video_decoder));
     uses_oop_video_decoder = true;
   } else {
+    DCHECK(frame_pool);
 #if BUILDFLAG(USE_VAAPI)
     create_decoder_function_cb = base::BindOnce(&VaapiVideoDecoder::Create);
 #elif BUILDFLAG(USE_V4L2_CODEC)
@@ -459,14 +460,16 @@
   CHECK(decoder_reservation_);
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
   DETACH_FROM_SEQUENCE(decoder_sequence_checker_);
-  DCHECK(main_frame_pool_);
+  DCHECK_EQ(!main_frame_pool_, uses_oop_video_decoder_);
   DCHECK(client_task_runner_);
   CHECK(frame_converter_);
   DVLOGF(2);
 
   decoder_weak_this_ = decoder_weak_this_factory_.GetWeakPtr();
 
-  main_frame_pool_->set_parent_task_runner(decoder_task_runner_);
+  if (main_frame_pool_) {
+    main_frame_pool_->set_parent_task_runner(decoder_task_runner_);
+  }
   frame_converter_->Initialize(
       decoder_task_runner_,
       base::BindRepeating(&VideoDecoderPipeline::OnFrameConverted,
@@ -1088,6 +1091,7 @@
   // is the largest amount of reference frames seen, on an ITU-T H.264 test
   // vector (CAPCM*1_Sand_E.h264).
   CHECK_LE(num_codec_reference_frames, 32u);
+  CHECK(!uses_oop_video_decoder_);
 
   if (candidates.empty())
     return CroStatus::Codes::kNoDecoderOutputFormatCandidates;
diff --git a/media/mojo/mojom/media_types.mojom b/media/mojo/mojom/media_types.mojom
index 04fad963..815c46d0 100644
--- a/media/mojo/mojom/media_types.mojom
+++ b/media/mojo/mojom/media_types.mojom
@@ -120,11 +120,7 @@
 };
 
 // See media/base/svc_scalability_mode.h for description.
-// This mojo enum only list hardware codec supported scalability mode.
 enum SVCScalabilityMode {
-  // kUnsupportedMode is used to handle the enum differ of C++ and Mojo
-  // SVCScalabilityMode in ToMojom, should not be used in other place.
-  kUnsupportedMode,
   kL1T1,
   kL1T2,
   kL1T3,
@@ -134,18 +130,34 @@
   kL3T1,
   kL3T2,
   kL3T3,
-  kL2T1Key,
-  kL2T2Key,
-  kL2T3Key,
-  kL3T1Key,
-  kL3T2Key,
-  kL3T3Key,
+  kL2T1h,
+  kL2T2h,
+  kL2T3h,
   kS2T1,
   kS2T2,
   kS2T3,
+  kS2T1h,
+  kS2T2h,
+  kS2T3h,
   kS3T1,
   kS3T2,
   kS3T3,
+  kS3T1h,
+  kS3T2h,
+  kS3T3h,
+  kL2T1Key,
+  kL2T2Key,
+  kL2T2KeyShift,
+  kL2T3Key,
+  kL2T3KeyShift,
+  kL3T1Key,
+  kL3T2Key,
+  kL3T2KeyShift,
+  kL3T3Key,
+  kL3T3KeyShift,
+  kL3T1h,
+  kL3T2h,
+  kL3T3h,
 };
 
 // This mojo enum only list hardware codec supported scalability mode.
diff --git a/media/mojo/mojom/media_types_enum_mojom_traits.h b/media/mojo/mojom/media_types_enum_mojom_traits.h
index edb001b..699d7380 100644
--- a/media/mojo/mojom/media_types_enum_mojom_traits.h
+++ b/media/mojo/mojom/media_types_enum_mojom_traits.h
@@ -173,55 +173,69 @@
         return media::mojom::SVCScalabilityMode::kL3T2;
       case media::SVCScalabilityMode::kL3T3:
         return media::mojom::SVCScalabilityMode::kL3T3;
-      case media::SVCScalabilityMode::kL2T1Key:
-        return media::mojom::SVCScalabilityMode::kL2T1Key;
-      case media::SVCScalabilityMode::kL2T2Key:
-        return media::mojom::SVCScalabilityMode::kL2T2Key;
-      case media::SVCScalabilityMode::kL2T3Key:
-        return media::mojom::SVCScalabilityMode::kL2T3Key;
-      case media::SVCScalabilityMode::kL3T1Key:
-        return media::mojom::SVCScalabilityMode::kL3T1Key;
-      case media::SVCScalabilityMode::kL3T2Key:
-        return media::mojom::SVCScalabilityMode::kL3T2Key;
-      case media::SVCScalabilityMode::kL3T3Key:
-        return media::mojom::SVCScalabilityMode::kL3T3Key;
+      case media::SVCScalabilityMode::kL2T1h:
+        return media::mojom::SVCScalabilityMode::kL2T1h;
+      case media::SVCScalabilityMode::kL2T2h:
+        return media::mojom::SVCScalabilityMode::kL2T2h;
+      case media::SVCScalabilityMode::kL2T3h:
+        return media::mojom::SVCScalabilityMode::kL2T3h;
       case media::SVCScalabilityMode::kS2T1:
         return media::mojom::SVCScalabilityMode::kS2T1;
       case media::SVCScalabilityMode::kS2T2:
         return media::mojom::SVCScalabilityMode::kS2T2;
       case media::SVCScalabilityMode::kS2T3:
         return media::mojom::SVCScalabilityMode::kS2T3;
+      case media::SVCScalabilityMode::kS2T1h:
+        return media::mojom::SVCScalabilityMode::kS2T1h;
+      case media::SVCScalabilityMode::kS2T2h:
+        return media::mojom::SVCScalabilityMode::kS2T2h;
+      case media::SVCScalabilityMode::kS2T3h:
+        return media::mojom::SVCScalabilityMode::kS2T3h;
       case media::SVCScalabilityMode::kS3T1:
         return media::mojom::SVCScalabilityMode::kS3T1;
       case media::SVCScalabilityMode::kS3T2:
         return media::mojom::SVCScalabilityMode::kS3T2;
       case media::SVCScalabilityMode::kS3T3:
         return media::mojom::SVCScalabilityMode::kS3T3;
-      case media::SVCScalabilityMode::kL2T1h:
-      case media::SVCScalabilityMode::kL2T2h:
-      case media::SVCScalabilityMode::kL2T3h:
-      case media::SVCScalabilityMode::kS2T1h:
-      case media::SVCScalabilityMode::kS2T2h:
-      case media::SVCScalabilityMode::kS2T3h:
       case media::SVCScalabilityMode::kS3T1h:
+        return media::mojom::SVCScalabilityMode::kS3T1h;
       case media::SVCScalabilityMode::kS3T2h:
+        return media::mojom::SVCScalabilityMode::kS3T2h;
       case media::SVCScalabilityMode::kS3T3h:
+        return media::mojom::SVCScalabilityMode::kS3T3h;
+      case media::SVCScalabilityMode::kL2T1Key:
+        return media::mojom::SVCScalabilityMode::kL2T1Key;
+      case media::SVCScalabilityMode::kL2T2Key:
+        return media::mojom::SVCScalabilityMode::kL2T2Key;
       case media::SVCScalabilityMode::kL2T2KeyShift:
+        return media::mojom::SVCScalabilityMode::kL2T2KeyShift;
+      case media::SVCScalabilityMode::kL2T3Key:
+        return media::mojom::SVCScalabilityMode::kL2T3Key;
       case media::SVCScalabilityMode::kL2T3KeyShift:
+        return media::mojom::SVCScalabilityMode::kL2T3KeyShift;
+      case media::SVCScalabilityMode::kL3T1Key:
+        return media::mojom::SVCScalabilityMode::kL3T1Key;
+      case media::SVCScalabilityMode::kL3T2Key:
+        return media::mojom::SVCScalabilityMode::kL3T2Key;
       case media::SVCScalabilityMode::kL3T2KeyShift:
+        return media::mojom::SVCScalabilityMode::kL3T2KeyShift;
+      case media::SVCScalabilityMode::kL3T3Key:
+        return media::mojom::SVCScalabilityMode::kL3T3Key;
       case media::SVCScalabilityMode::kL3T3KeyShift:
+        return media::mojom::SVCScalabilityMode::kL3T3KeyShift;
       case media::SVCScalabilityMode::kL3T1h:
+        return media::mojom::SVCScalabilityMode::kL3T1h;
       case media::SVCScalabilityMode::kL3T2h:
+        return media::mojom::SVCScalabilityMode::kL3T2h;
       case media::SVCScalabilityMode::kL3T3h:
-        NOTREACHED();
+        return media::mojom::SVCScalabilityMode::kL3T3h;
     }
+    NOTREACHED();
   }
 
   static bool FromMojom(media::mojom::SVCScalabilityMode input,
                         media::SVCScalabilityMode* output) {
     switch (input) {
-      case media::mojom::SVCScalabilityMode::kUnsupportedMode:
-        NOTREACHED();
       case media::mojom::SVCScalabilityMode::kL1T1:
         *output = media::SVCScalabilityMode::kL1T1;
         return true;
@@ -249,23 +263,14 @@
       case media::mojom::SVCScalabilityMode::kL3T3:
         *output = media::SVCScalabilityMode::kL3T3;
         return true;
-      case media::mojom::SVCScalabilityMode::kL2T1Key:
-        *output = media::SVCScalabilityMode::kL2T1Key;
+      case media::mojom::SVCScalabilityMode::kL2T1h:
+        *output = media::SVCScalabilityMode::kL2T1h;
         return true;
-      case media::mojom::SVCScalabilityMode::kL2T2Key:
-        *output = media::SVCScalabilityMode::kL2T2Key;
+      case media::mojom::SVCScalabilityMode::kL2T2h:
+        *output = media::SVCScalabilityMode::kL2T2h;
         return true;
-      case media::mojom::SVCScalabilityMode::kL2T3Key:
-        *output = media::SVCScalabilityMode::kL2T3Key;
-        return true;
-      case media::mojom::SVCScalabilityMode::kL3T1Key:
-        *output = media::SVCScalabilityMode::kL3T1Key;
-        return true;
-      case media::mojom::SVCScalabilityMode::kL3T2Key:
-        *output = media::SVCScalabilityMode::kL3T2Key;
-        return true;
-      case media::mojom::SVCScalabilityMode::kL3T3Key:
-        *output = media::SVCScalabilityMode::kL3T3Key;
+      case media::mojom::SVCScalabilityMode::kL2T3h:
+        *output = media::SVCScalabilityMode::kL2T3h;
         return true;
       case media::mojom::SVCScalabilityMode::kS2T1:
         *output = media::SVCScalabilityMode::kS2T1;
@@ -276,6 +281,15 @@
       case media::mojom::SVCScalabilityMode::kS2T3:
         *output = media::SVCScalabilityMode::kS2T3;
         return true;
+      case media::mojom::SVCScalabilityMode::kS2T1h:
+        *output = media::SVCScalabilityMode::kS2T1h;
+        return true;
+      case media::mojom::SVCScalabilityMode::kS2T2h:
+        *output = media::SVCScalabilityMode::kS2T2h;
+        return true;
+      case media::mojom::SVCScalabilityMode::kS2T3h:
+        *output = media::SVCScalabilityMode::kS2T3h;
+        return true;
       case media::mojom::SVCScalabilityMode::kS3T1:
         *output = media::SVCScalabilityMode::kS3T1;
         return true;
@@ -285,9 +299,57 @@
       case media::mojom::SVCScalabilityMode::kS3T3:
         *output = media::SVCScalabilityMode::kS3T3;
         return true;
+      case media::mojom::SVCScalabilityMode::kS3T1h:
+        *output = media::SVCScalabilityMode::kS3T1h;
+        return true;
+      case media::mojom::SVCScalabilityMode::kS3T2h:
+        *output = media::SVCScalabilityMode::kS3T2h;
+        return true;
+      case media::mojom::SVCScalabilityMode::kS3T3h:
+        *output = media::SVCScalabilityMode::kS3T3h;
+        return true;
+      case media::mojom::SVCScalabilityMode::kL2T1Key:
+        *output = media::SVCScalabilityMode::kL2T1Key;
+        return true;
+      case media::mojom::SVCScalabilityMode::kL2T2Key:
+        *output = media::SVCScalabilityMode::kL2T2Key;
+        return true;
+      case media::mojom::SVCScalabilityMode::kL2T2KeyShift:
+        *output = media::SVCScalabilityMode::kL2T2KeyShift;
+        return true;
+      case media::mojom::SVCScalabilityMode::kL2T3Key:
+        *output = media::SVCScalabilityMode::kL2T3Key;
+        return true;
+      case media::mojom::SVCScalabilityMode::kL2T3KeyShift:
+        *output = media::SVCScalabilityMode::kL2T3KeyShift;
+        return true;
+      case media::mojom::SVCScalabilityMode::kL3T1Key:
+        *output = media::SVCScalabilityMode::kL3T1Key;
+        return true;
+      case media::mojom::SVCScalabilityMode::kL3T2Key:
+        *output = media::SVCScalabilityMode::kL3T2Key;
+        return true;
+      case media::mojom::SVCScalabilityMode::kL3T2KeyShift:
+        *output = media::SVCScalabilityMode::kL3T2KeyShift;
+        return true;
+      case media::mojom::SVCScalabilityMode::kL3T3Key:
+        *output = media::SVCScalabilityMode::kL3T3Key;
+        return true;
+      case media::mojom::SVCScalabilityMode::kL3T3KeyShift:
+        *output = media::SVCScalabilityMode::kL3T3KeyShift;
+        return true;
+      case media::mojom::SVCScalabilityMode::kL3T1h:
+        *output = media::SVCScalabilityMode::kL3T1h;
+        return true;
+      case media::mojom::SVCScalabilityMode::kL3T2h:
+        *output = media::SVCScalabilityMode::kL3T2h;
+        return true;
+      case media::mojom::SVCScalabilityMode::kL3T3h:
+        *output = media::SVCScalabilityMode::kL3T3h;
+        return true;
     }
-
     NOTREACHED();
+    return false;
   }
 };
 
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn
index 9f54d77..d646f78b 100644
--- a/media/mojo/services/BUILD.gn
+++ b/media/mojo/services/BUILD.gn
@@ -93,6 +93,7 @@
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/system",
     "//services/service_manager/public/cpp",
+    "//services/viz/public/cpp/gpu",
     "//url",
   ]
 
diff --git a/media/mojo/services/DEPS b/media/mojo/services/DEPS
index 15f5d5a..ebf32a9 100644
--- a/media/mojo/services/DEPS
+++ b/media/mojo/services/DEPS
@@ -14,9 +14,11 @@
   "oop_video_decoder_factory_service\.cc": [
     "+components/viz/common/switches.h",
   ],
+  "oop_video_decoder_factory_process_service\.cc": [
+    "+services/viz/public/cpp/gpu/gpu.h",
+  ],
   "webrtc_video_perf_mojolpm_fuzzer\.cc": [
     "+third_party/libprotobuf-mutator/src/src",
     "+components/leveldb_proto/testing/fake_db.h",
   ],
 }
-
diff --git a/media/mojo/services/gpu_mojo_media_client_cros.cc b/media/mojo/services/gpu_mojo_media_client_cros.cc
index 8079e5b3..564cd24e 100644
--- a/media/mojo/services/gpu_mojo_media_client_cros.cc
+++ b/media/mojo/services/gpu_mojo_media_client_cros.cc
@@ -4,6 +4,7 @@
 
 #include "media/mojo/services/gpu_mojo_media_client.h"
 
+#include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/task/sequenced_task_runner.h"
 #include "chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.h"
@@ -13,6 +14,7 @@
 #include "media/base/media_switches.h"
 #include "media/gpu/chromeos/mailbox_video_frame_converter.h"
 #include "media/gpu/chromeos/platform_video_frame_pool.h"
+#include "media/gpu/chromeos/simple_video_frame_converter.h"
 #include "media/gpu/chromeos/video_decoder_pipeline.h"
 
 namespace media {
@@ -65,15 +67,13 @@
 
     switch (decoder_type) {
       case VideoDecoderType::kOutOfProcess: {
-        // TODO(b/195769334): for out-of-process video decoding, we don't need a
-        // |frame_pool| because the buffers will be allocated and managed
-        // out-of-process.
-        auto frame_pool = std::make_unique<PlatformVideoFramePool>();
-
-        auto frame_converter = MailboxVideoFrameConverter::Create(
-            gpu_task_runner_, traits.get_command_buffer_stub_cb);
+        auto frame_converter =
+            base::FeatureList::IsEnabled(kUseSharedImageInOOPVDProcess)
+                ? SimpleVideoFrameConverter::Create()
+                : MailboxVideoFrameConverter::Create(
+                      gpu_task_runner_, traits.get_command_buffer_stub_cb);
         return VideoDecoderPipeline::Create(
-            gpu_workarounds_, traits.task_runner, std::move(frame_pool),
+            gpu_workarounds_, traits.task_runner, /*frame_pool=*/nullptr,
             std::move(frame_converter),
             GetPreferredRenderableFourccs(gpu_preferences_),
             traits.media_log->Clone(), std::move(traits.oop_video_decoder),
diff --git a/media/mojo/services/gpu_mojo_media_client_linux.cc b/media/mojo/services/gpu_mojo_media_client_linux.cc
index 83cdb12..4c7edca 100644
--- a/media/mojo/services/gpu_mojo_media_client_linux.cc
+++ b/media/mojo/services/gpu_mojo_media_client_linux.cc
@@ -13,6 +13,7 @@
 #include "media/base/media_switches.h"
 #include "media/gpu/chromeos/mailbox_video_frame_converter.h"
 #include "media/gpu/chromeos/platform_video_frame_pool.h"
+#include "media/gpu/chromeos/simple_video_frame_converter.h"
 #include "media/gpu/chromeos/video_decoder_pipeline.h"
 #include "ui/gfx/buffer_types.h"
 
@@ -23,9 +24,7 @@
 BASE_FEATURE(kAcceleratedVideoDecodeLinuxZeroCopyGL,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kRenderableMM21,
-             "RenderableMM21",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kRenderableMM21, base::FEATURE_DISABLED_BY_DEFAULT);
 
 VideoDecoderType GetPreferredLinuxDecoderImplementation() {
   // VaapiVideoDecoder flag is required for VaapiVideoDecoder.
@@ -185,15 +184,13 @@
 
     switch (decoder_type) {
       case VideoDecoderType::kOutOfProcess: {
-        // TODO(b/195769334): for out-of-process video decoding, we don't need a
-        // |frame_pool| because the buffers will be allocated and managed
-        // out-of-process.
-        auto frame_pool = std::make_unique<PlatformVideoFramePool>();
-
-        auto frame_converter = MailboxVideoFrameConverter::Create(
-            gpu_task_runner_, traits.get_command_buffer_stub_cb);
+        auto frame_converter =
+            base::FeatureList::IsEnabled(kUseSharedImageInOOPVDProcess)
+                ? SimpleVideoFrameConverter::Create()
+                : MailboxVideoFrameConverter::Create(
+                      gpu_task_runner_, traits.get_command_buffer_stub_cb);
         return VideoDecoderPipeline::Create(
-            gpu_workarounds_, traits.task_runner, std::move(frame_pool),
+            gpu_workarounds_, traits.task_runner, /*frame_pool=*/nullptr,
             std::move(frame_converter),
             GetPreferredRenderableFourccs(gpu_preferences_, gpu_feature_info_),
             traits.media_log->Clone(), std::move(traits.oop_video_decoder),
diff --git a/media/mojo/services/oop_video_decoder_factory_process_service.cc b/media/mojo/services/oop_video_decoder_factory_process_service.cc
index 86915b3..04732ce3 100644
--- a/media/mojo/services/oop_video_decoder_factory_process_service.cc
+++ b/media/mojo/services/oop_video_decoder_factory_process_service.cc
@@ -4,16 +4,26 @@
 
 #include "media/mojo/services/oop_video_decoder_factory_process_service.h"
 
+#include "base/functional/bind.h"
+#include "gpu/ipc/client/client_shared_image_interface.h"
+#include "media/base/media_switches.h"
+#include "services/viz/public/cpp/gpu/gpu.h"
+
 namespace media {
 
 OOPVideoDecoderFactoryProcessService::OOPVideoDecoderFactoryProcessService(
     mojo::PendingReceiver<mojom::VideoDecoderFactoryProcess> receiver)
-    : receiver_(this, std::move(receiver)) {
+    : receiver_(this, std::move(receiver)),
+      task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
 OOPVideoDecoderFactoryProcessService::~OOPVideoDecoderFactoryProcessService() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (shared_image_interface_ &&
+      !shared_image_interface_->gpu_channel()->IsLost()) {
+    shared_image_interface_->gpu_channel()->RemoveObserver(this);
+  }
 }
 
 void OOPVideoDecoderFactoryProcessService::InitializeVideoDecoderFactory(
@@ -23,7 +33,8 @@
 
   // The browser process ensures this is called only once.
   DCHECK(!factory_);
-  factory_ = std::make_unique<OOPVideoDecoderFactoryService>(gpu_feature_info);
+  factory_ = std::make_unique<OOPVideoDecoderFactoryService>(
+      gpu_feature_info, GetSharedImageInterface());
 
   // base::Unretained(this) is safe here because the disconnection callback
   // won't run beyond the lifetime of |factory_| which is fully owned by
@@ -35,6 +46,55 @@
           base::Unretained(this)));
 }
 
+void OOPVideoDecoderFactoryProcessService::OnGpuChannelLost() {
+  // GpuChannel lost is notified on utility IO thread. Forward it to utility
+  // main and allow notification to finish.
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &OOPVideoDecoderFactoryProcessService::OnGpuChannelLostTask,
+          weak_ptr_factory_.GetWeakPtr()));
+}
+
+void OOPVideoDecoderFactoryProcessService::OnGpuChannelLostTask() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!shared_image_interface_) {
+    return;
+  }
+
+  // The Observable removes all observers after completing GpuChannelLost
+  // notification. No need to RemoveObserver(). Call RemoveObserver during
+  // notification will cause deadlock.
+  factory_->OnGpuChannelReestablished(GetSharedImageInterface());
+}
+
+scoped_refptr<gpu::ClientSharedImageInterface>
+OOPVideoDecoderFactoryProcessService::GetSharedImageInterface() {
+  if (!base::FeatureList::IsEnabled(kUseSharedImageInOOPVDProcess) ||
+      !viz_gpu_) {
+    return nullptr;
+  }
+  if (shared_image_interface_ &&
+      !shared_image_interface_->gpu_channel()->IsLost()) {
+    return shared_image_interface_;
+  }
+  shared_image_interface_.reset();
+  scoped_refptr<gpu::GpuChannelHost> gpu_channel_host =
+      viz_gpu_->EstablishGpuChannelSync();
+  if (!gpu_channel_host ||
+      !gpu_channel_host->AddObserverIfNotAlreadyLost(this)) {
+    return nullptr;
+  }
+  shared_image_interface_ =
+      gpu_channel_host->CreateClientSharedImageInterface();
+  return shared_image_interface_;
+}
+
+void OOPVideoDecoderFactoryProcessService::SetVizGpu(
+    std::unique_ptr<viz::Gpu> viz_gpu) {
+  viz_gpu_ = std::move(viz_gpu);
+}
+
 void OOPVideoDecoderFactoryProcessService::OnFactoryDisconnected() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/media/mojo/services/oop_video_decoder_factory_process_service.h b/media/mojo/services/oop_video_decoder_factory_process_service.h
index 21dabfd..1ecf36f 100644
--- a/media/mojo/services/oop_video_decoder_factory_process_service.h
+++ b/media/mojo/services/oop_video_decoder_factory_process_service.h
@@ -6,17 +6,27 @@
 #define MEDIA_MOJO_SERVICES_OOP_VIDEO_DECODER_FACTORY_PROCESS_SERVICE_H_
 
 #include "base/sequence_checker.h"
+#include "gpu/ipc/client/gpu_channel_observer.h"
 #include "media/mojo/mojom/video_decoder_factory_process.mojom.h"
 #include "media/mojo/services/media_mojo_export.h"
 #include "media/mojo/services/oop_video_decoder_factory_service.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
+namespace gpu {
+class ClientSharedImageInterface;
+}
+
+namespace viz {
+class Gpu;
+}
+
 namespace media {
 
 // An OOPVideoDecoderFactoryProcessService allows the browser process to
 // initialize an InterfaceFactory with a gpu::GpuFeatureInfo.
 class MEDIA_MOJO_EXPORT OOPVideoDecoderFactoryProcessService final
-    : public mojom::VideoDecoderFactoryProcess {
+    : public mojom::VideoDecoderFactoryProcess,
+      public gpu::GpuChannelLostObserver {
  public:
   explicit OOPVideoDecoderFactoryProcessService(
       mojo::PendingReceiver<mojom::VideoDecoderFactoryProcess> receiver);
@@ -31,6 +41,10 @@
       const gpu::GpuFeatureInfo& gpu_feature_info,
       mojo::PendingReceiver<mojom::InterfaceFactory> receiver) final;
 
+  // gpu::GpuChannelLostObserver implementation.
+  void OnGpuChannelLost() final;
+
+  void SetVizGpu(std::unique_ptr<viz::Gpu> viz_gpu);
   void OnFactoryDisconnected();
 
  private:
@@ -39,7 +53,16 @@
   std::unique_ptr<OOPVideoDecoderFactoryService> factory_
       GUARDED_BY_CONTEXT(sequence_checker_);
 
+  void OnGpuChannelLostTask();
+  scoped_refptr<gpu::ClientSharedImageInterface> GetSharedImageInterface();
+
+  scoped_refptr<gpu::ClientSharedImageInterface> shared_image_interface_;
+  std::unique_ptr<viz::Gpu> viz_gpu_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
   SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<OOPVideoDecoderFactoryProcessService> weak_ptr_factory_{
+      this};
 };
 
 }  // namespace media
diff --git a/media/mojo/services/oop_video_decoder_factory_service.cc b/media/mojo/services/oop_video_decoder_factory_service.cc
index 53866c1..f9c91c4 100644
--- a/media/mojo/services/oop_video_decoder_factory_service.cc
+++ b/media/mojo/services/oop_video_decoder_factory_service.cc
@@ -4,16 +4,21 @@
 
 #include "media/mojo/services/oop_video_decoder_factory_service.h"
 
+#include "base/feature_list.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "components/viz/common/switches.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_preferences.h"
+#include "gpu/ipc/client/client_shared_image_interface.h"
+#include "gpu/ipc/client/gpu_channel_host.h"
 #include "media/base/media_log.h"
+#include "media/base/media_switches.h"
 #include "media/base/media_util.h"
 #include "media/gpu/buildflags.h"
 #include "media/gpu/chromeos/dmabuf_video_frame_converter.h"
 #include "media/gpu/chromeos/frame_registry.h"
+#include "media/gpu/chromeos/mailbox_video_frame_converter.h"
 #include "media/gpu/chromeos/platform_video_frame_pool.h"
 #include "media/gpu/chromeos/video_decoder_pipeline.h"
 #include "media/gpu/gpu_video_accelerator_util.h"
@@ -34,9 +39,12 @@
 // like its |gpu_task_runner_| and |media_gpu_channel_manager_| members.
 class MojoMediaClientImpl : public MojoMediaClient {
  public:
-  explicit MojoMediaClientImpl(const gpu::GpuFeatureInfo& gpu_feature_info)
+  explicit MojoMediaClientImpl(
+      const gpu::GpuFeatureInfo& gpu_feature_info,
+      scoped_refptr<gpu::ClientSharedImageInterface> sii)
       : gpu_driver_bug_workarounds_(
-            gpu_feature_info.enabled_gpu_driver_bug_workarounds) {}
+            gpu_feature_info.enabled_gpu_driver_bug_workarounds),
+        shared_image_interface_(std::move(sii)) {}
   MojoMediaClientImpl(const MojoMediaClientImpl&) = delete;
   MojoMediaClientImpl& operator=(const MojoMediaClientImpl&) = delete;
   ~MojoMediaClientImpl() override = default;
@@ -86,28 +94,44 @@
                   : std::make_unique<media::NullMediaLog>();
 
     CHECK_NE(GetDecoderImplementationType(), VideoDecoderType::kVda);
+    auto sii = shared_image_interface_ &&
+                       !shared_image_interface_->gpu_channel()->IsLost()
+                   ? shared_image_interface_
+                   : nullptr;
+    auto converter = base::FeatureList::IsEnabled(kUseSharedImageInOOPVDProcess)
+                         ? MailboxVideoFrameConverter::Create(std::move(sii))
+                         : DmabufVideoFrameConverter::Create();
     return VideoDecoderPipeline::Create(
         gpu_driver_bug_workarounds_,
         /*client_task_runner=*/std::move(task_runner),
-        std::make_unique<PlatformVideoFramePool>(),
-        DmabufVideoFrameConverter::Create(),
+        std::make_unique<PlatformVideoFramePool>(), std::move(converter),
         VideoDecoderPipeline::DefaultPreferredRenderableFourccs(),
         std::move(log),
         /*oop_video_decoder=*/{},
         /*in_video_decoder_process=*/true);
   }
+  // Old `VideoDecoder`s will automatically destruct with the ipc pipe. Setting
+  // a new sii ensures future `VideoDecoder`s can send buffers to the correct
+  // gpu process.
+  void OnGpuChannelReestablished(
+      scoped_refptr<gpu::ClientSharedImageInterface> new_sii) {
+    shared_image_interface_ = new_sii;
+  }
 
  private:
   const gpu::GpuDriverBugWorkarounds gpu_driver_bug_workarounds_;
+  scoped_refptr<gpu::ClientSharedImageInterface> shared_image_interface_;
 };
 
 }  // namespace
 
 OOPVideoDecoderFactoryService::OOPVideoDecoderFactoryService(
-    const gpu::GpuFeatureInfo& gpu_feature_info)
+    const gpu::GpuFeatureInfo& gpu_feature_info,
+    scoped_refptr<gpu::ClientSharedImageInterface> sii)
     : receiver_(this),
+      shared_image_interface_(sii),
       mojo_media_client_(
-          std::make_unique<MojoMediaClientImpl>(gpu_feature_info)) {
+          std::make_unique<MojoMediaClientImpl>(gpu_feature_info, sii)) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   mojo_media_client_->Initialize();
 }
@@ -180,4 +204,12 @@
   NOTREACHED();
 }
 
+void OOPVideoDecoderFactoryService::OnGpuChannelReestablished(
+    scoped_refptr<gpu::ClientSharedImageInterface> new_sii) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  shared_image_interface_ = new_sii;
+  static_cast<MojoMediaClientImpl*>(mojo_media_client_.get())
+      ->OnGpuChannelReestablished(new_sii);
+}
+
 }  // namespace media
diff --git a/media/mojo/services/oop_video_decoder_factory_service.h b/media/mojo/services/oop_video_decoder_factory_service.h
index 883ae96a..6346928 100644
--- a/media/mojo/services/oop_video_decoder_factory_service.h
+++ b/media/mojo/services/oop_video_decoder_factory_service.h
@@ -16,6 +16,10 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/unique_receiver_set.h"
 
+namespace gpu {
+class ClientSharedImageInterface;
+}
+
 namespace media {
 namespace mojom {
 class VideoDecoder;
@@ -36,7 +40,9 @@
     : public mojom::InterfaceFactory {
  public:
   explicit OOPVideoDecoderFactoryService(
-      const gpu::GpuFeatureInfo& gpu_feature_info);
+      const gpu::GpuFeatureInfo& gpu_feature_info,
+      scoped_refptr<gpu::ClientSharedImageInterface> sii);
+
   OOPVideoDecoderFactoryService(const OOPVideoDecoderFactoryService&) = delete;
   OOPVideoDecoderFactoryService& operator=(
       const OOPVideoDecoderFactoryService&) = delete;
@@ -75,12 +81,19 @@
   void CreateCdm(const CdmConfig& cdm_config,
                  CreateCdmCallback callback) override;
 
+  // `shared_image_interface_` is stale on gpu channel loss, reset it for future
+  // decoders.
+  void OnGpuChannelReestablished(
+      scoped_refptr<gpu::ClientSharedImageInterface> new_sii);
+
  private:
   VideoDecoderCreationCBForTesting video_decoder_creation_cb_for_testing_
       GUARDED_BY_CONTEXT(sequence_checker_);
 
   mojo::Receiver<mojom::InterfaceFactory> receiver_;
 
+  scoped_refptr<gpu::ClientSharedImageInterface> shared_image_interface_;
+
   // |mojo_media_client_| and |cdm_service_context_| must be declared before
   // |video_decoders_| because the interface implementation instances managed by
   // that set take raw pointers to them.
diff --git a/media/mojo/services/oop_video_decoder_service_unittest.cc b/media/mojo/services/oop_video_decoder_service_unittest.cc
index 3d3700a..495baaf1 100644
--- a/media/mojo/services/oop_video_decoder_service_unittest.cc
+++ b/media/mojo/services/oop_video_decoder_service_unittest.cc
@@ -9,6 +9,8 @@
 #include "base/posix/eintr_wrapper.h"
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
+#include "gpu/ipc/client/client_shared_image_interface.h"
+#include "gpu/ipc/client/gpu_channel_host.h"
 #include "media/mojo/common/media_type_converters.h"
 #include "media/mojo/common/mojo_decoder_buffer_converter.h"
 #include "media/mojo/mojom/media_log.mojom.h"
@@ -19,6 +21,7 @@
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/gpu_fence.h"
 
 using testing::_;
 using testing::ByMove;
@@ -342,7 +345,7 @@
 class OOPVideoDecoderServiceTest : public testing::Test {
  public:
   OOPVideoDecoderServiceTest()
-      : oop_video_decoder_factory_service_(gpu::GpuFeatureInfo()) {
+      : oop_video_decoder_factory_service_(gpu::GpuFeatureInfo(), nullptr) {
     oop_video_decoder_factory_service_
         .SetVideoDecoderCreationCallbackForTesting(
             video_decoder_creation_cb_.Get());
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index 875596e..e11259b3 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -882,22 +882,17 @@
                                          kPremul_SkAlphaType,
                                          color_space.ToSkColorSpace())) {
     raster_context_provider_ = std::move(raster_context_provider);
+    CHECK(raster_context_provider_->ContextCapabilities().gpu_rasterization);
     auto* sii = raster_context_provider_->SharedImageInterface();
 
-    // This SI is used to cache the VideoFrame. We will eventually read out
-    // its contents into a destination GL texture via the GLES2 interface.
+    // This SI is used to cache the VideoFrame. We copy the contents of the
+    // source VideoFrame into the cached SI over the raster interface and will
+    // eventually read out its contents into a destination GL texture via the
+    // GLES2 interface.
     gpu::SharedImageUsageSet flags = gpu::SHARED_IMAGE_USAGE_GLES2_READ |
-                                     gpu::SHARED_IMAGE_USAGE_RASTER_READ;
-    // We copy the contents of the source VideoFrame *into* the
-    // cached SI over the raster interface - the usage bits depend on
-    // whether OOP-Raster is enabled.
-    flags |= gpu::SHARED_IMAGE_USAGE_RASTER_WRITE;
-    if (raster_context_provider_->ContextCapabilities().gpu_rasterization) {
-      flags |= gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
-    } else {
-      flags |= gpu::SHARED_IMAGE_USAGE_GLES2_WRITE;
-    }
-
+                                     gpu::SHARED_IMAGE_USAGE_RASTER_READ |
+                                     gpu::SHARED_IMAGE_USAGE_RASTER_WRITE |
+                                     gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
     shared_image_ =
         sii->CreateSharedImage({SHARED_IMAGE_FORMAT, coded_size, color_space,
                                 flags, "PaintCanvasVideoRenderer"},
@@ -924,12 +919,10 @@
     return raster_context_provider_;
   }
 
-  bool BeginAccess(gpu::raster::RasterInterface* ri) {
+  void BeginAccess(gpu::raster::RasterInterface* ri) {
     CHECK(!ri_access_);
-    CHECK(raster_context_provider()->ContextCapabilities().gpu_rasterization);
     ri_access_ =
         shared_image_->BeginRasterAccess(ri, sync_token_, /*readonly=*/true);
-    return true;
   }
 
   void clear_access() {
@@ -977,17 +970,6 @@
                                    /*plane_index=*/0, dst_pixels);
   }
 
-  void FlushPendingSkiaOps() override {
-    if (!raster_context_provider_ || !sk_image_) {
-      return;
-    }
-    GrDirectContext* ctx = raster_context_provider_->GrContext();
-    if (!ctx) {
-      return;
-    }
-    ctx->flushAndSubmit(sk_image_);
-  }
-
   const gpu::SyncToken& sync_token() { return sync_token_; }
   void UpdateSyncToken(const gpu::SyncToken& sync_token) {
     sync_token_ = sync_token;
@@ -1032,11 +1014,9 @@
           << "Can't render textured frames w/o viz::RasterContextProvider";
       return;  // Unable to get/create a shared main thread context.
     }
-    if (!raster_context_provider->GrContext() &&
-        !raster_context_provider->ContextCapabilities().gpu_rasterization) {
-      DLOG(ERROR)
-          << "Can't render textured frames w/o valid GrContext or GPU raster.";
-      return;  // The context has been lost.
+    if (!raster_context_provider->ContextCapabilities().gpu_rasterization) {
+      DLOG(ERROR) << "Can't render textured frames w/o GPU raster.";
+      return;
     }
   }
 
@@ -1546,10 +1526,7 @@
   if (!raster_context_provider) {
     return false;
   }
-  GrDirectContext* gr_context = raster_context_provider->GrContext();
-  const bool gpu_rasterization =
-      raster_context_provider->ContextCapabilities().gpu_rasterization;
-  if (!gr_context && !gpu_rasterization) {
+  if (!raster_context_provider->ContextCapabilities().gpu_rasterization) {
     return false;
   }
   gpu::raster::RasterInterface* canvas_ri =
@@ -1562,19 +1539,14 @@
     rgb_shared_image_cache_ = std::make_unique<VideoFrameSharedImageCache>();
   }
 
-  // This SI is used to cache the VideoFrame. We will eventually read out
-  // its contents into a destination GL texture via the GLES2 interface.
+  // This SI is used to cache the VideoFrame. We copy the contents of the source
+  // VideoFrame into the cached SI over the raster interface and will eventually
+  // read out its contents into a destination GL texture via the GLES2
+  // interface.
   gpu::SharedImageUsageSet src_usage =
-      gpu::SHARED_IMAGE_USAGE_GLES2_READ | gpu::SHARED_IMAGE_USAGE_RASTER_WRITE;
-  // We copy the contents of the source VideoFrame *into* the cached SI over the
-  // raster interface - the usage bits depend on whether OOP-Raster is enabled.
-  // TODO(crbug.com/40194377): Always use OOP_RASTERIZATION usage once OOP-C is
-  // fully launched.
-  if (gpu_rasterization) {
-    src_usage |= gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
-  } else {
-    src_usage |= gpu::SHARED_IMAGE_USAGE_GLES2_WRITE;
-  }
+      gpu::SHARED_IMAGE_USAGE_GLES2_READ |
+      gpu::SHARED_IMAGE_USAGE_RASTER_WRITE |
+      gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
   auto [rgb_shared_image, rgb_sync_token, status] =
       rgb_shared_image_cache_->GetOrCreateSharedImage(
           video_frame.get(), raster_context_provider, src_usage,
@@ -1819,13 +1791,7 @@
 
 bool PaintCanvasVideoRenderer::Cache::Recycle() {
   paint_image = cc::PaintImage();
-  if (!texture_backing->unique()) {
-    return false;
-  }
-
-  // Flush any pending GPU work using this texture.
-  texture_backing->FlushPendingSkiaOps();
-  return true;
+  return texture_backing->unique();
 }
 
 bool PaintCanvasVideoRenderer::UpdateLastImage(
@@ -1850,10 +1816,8 @@
   // Holding |video_frame| longer than this call when using GPUVideoDecoder
   // could cause problems since the pool of VideoFrames has a fixed size.
   if (video_frame->HasSharedImage()) {
-    DCHECK(raster_context_provider);
-    bool gpu_rasterization =
-        raster_context_provider->ContextCapabilities().gpu_rasterization;
-    DCHECK(gpu_rasterization || raster_context_provider->GrContext());
+    CHECK(raster_context_provider);
+    CHECK(raster_context_provider->ContextCapabilities().gpu_rasterization);
     auto* ri = raster_context_provider->RasterInterface();
     DCHECK(ri);
     const auto video_frame_si = video_frame->shared_image();
@@ -1892,10 +1856,6 @@
         video_frame_si->mailbox(), client_shared_image->mailbox(), 0, 0, 0, 0,
         video_frame->coded_size().width(), video_frame->coded_size().height());
 
-    if (!gpu_rasterization) {
-      raster_context_provider->GrContext()->flushAndSubmit();
-    }
-
     // Ensure that |video_frame| not be deleted until the above copy is
     // completed.
     SynchronizeVideoFrameRead(video_frame, ri,
@@ -1907,16 +1867,9 @@
 
     cache_->coded_size = video_frame->coded_size();
 
-    // In OOPR mode, we can keep the entire TextureBacking. In non-OOPR,
-    // we can recycle the mailbox/texture, but have to replace the SkImage.
     paint_image_builder.set_texture_backing(cache_->texture_backing,
                                             cc::PaintImage::GetNextContentId());
-
-    bool success = cache_->texture_backing->BeginAccess(ri);
-    if (!success) {
-      cache_.reset();
-      return false;
-    }
+    cache_->texture_backing->BeginAccess(ri);
   } else {
     cache_.emplace(video_frame->unique_id());
     paint_image_builder.set_paint_image_generator(
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index a69a08b..51e190f 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -131,23 +131,6 @@
   return timeline_offset;
 }
 
-#if BUILDFLAG(IS_MAC)
-class ScopedVerboseLogEnabler {
- public:
-  ScopedVerboseLogEnabler() : old_level_(logging::GetMinLogLevel()) {
-    logging::SetMinLogLevel(-1);
-  }
-
-  ScopedVerboseLogEnabler(const ScopedVerboseLogEnabler&) = delete;
-  ScopedVerboseLogEnabler& operator=(const ScopedVerboseLogEnabler&) = delete;
-
-  ~ScopedVerboseLogEnabler() { logging::SetMinLogLevel(old_level_); }
-
- private:
-  const int old_level_;
-};
-#endif
-
 enum PromiseResult { RESOLVED, REJECTED };
 
 // Provides the test key in response to the encrypted event.
@@ -736,11 +719,6 @@
 }
 
 TEST_F(PipelineIntegrationTest, PlaybackWithAudioTrackDisabledThenEnabled) {
-#if BUILDFLAG(IS_MAC)
-  // Enable scoped logs to help track down hangs.  http://crbug.com/1014646
-  ScopedVerboseLogEnabler scoped_log_enabler;
-#endif
-
   ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm", kHashed | kNoClockless));
 
   // Disable audio.
@@ -770,11 +748,6 @@
 }
 
 TEST_F(PipelineIntegrationTest, PlaybackWithVideoTrackDisabledThenEnabled) {
-#if BUILDFLAG(IS_MAC)
-  // Enable scoped logs to help track down hangs.  http://crbug.com/1014646
-  ScopedVerboseLogEnabler scoped_log_enabler;
-#endif
-
   ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm", kHashed | kNoClockless));
 
   // Disable video.
@@ -2684,11 +2657,6 @@
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 TEST_F(PipelineIntegrationTest, SeekWhilePaused) {
-#if BUILDFLAG(IS_MAC)
-  // Enable scoped logs to help track down hangs.  http://crbug.com/1014646
-  ScopedVerboseLogEnabler scoped_log_enabler;
-#endif
-
   // This test is flaky without kNoClockless, see crbug.com/796250.
   ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm", kNoClockless));
 
@@ -2713,11 +2681,6 @@
 }
 
 TEST_F(PipelineIntegrationTest, SeekWhilePlaying) {
-#if BUILDFLAG(IS_MAC)
-  // Enable scoped logs to help track down hangs.  http://crbug.com/1014646
-  ScopedVerboseLogEnabler scoped_log_enabler;
-#endif
-
   // This test is flaky without kNoClockless, see crbug.com/796250.
   ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm", kNoClockless));
 
diff --git a/mojo/public/cpp/system/tests/invitation_unittest.cc b/mojo/public/cpp/system/tests/invitation_unittest.cc
index 854fbd25..35e4500 100644
--- a/mojo/public/cpp/system/tests/invitation_unittest.cc
+++ b/mojo/public/cpp/system/tests/invitation_unittest.cc
@@ -2,10 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
+#include "base/containers/span.h"
+
+
 
 #include "mojo/public/cpp/system/invitation.h"
 
@@ -99,8 +98,7 @@
 
  protected:
   void LaunchChildTestClient(const std::string& test_client_name,
-                             ScopedMessagePipeHandle* primordial_pipes,
-                             size_t num_primordial_pipes,
+                             base::span<ScopedMessagePipeHandle> primordial_pipes,
                              InvitationType invitation_type,
                              TransportType transport_type,
                              const ProcessErrorCallback& error_callback = {}) {
@@ -159,7 +157,7 @@
 
     OutgoingInvitation invitation;
     if (invitation_type != InvitationType::kIsolated) {
-      for (uint64_t name = 0; name < num_primordial_pipes; ++name)
+      for (uint64_t name = 0; name < primordial_pipes.size(); ++name)
         primordial_pipes[name] = invitation.AttachMessagePipe(name);
     }
 
@@ -185,8 +183,8 @@
                                    child_process_.Handle(),
                                    std::move(channel_endpoint), error_callback);
         } else {
-          DCHECK(primordial_pipes);
-          DCHECK_EQ(num_primordial_pipes, 1u);
+          DCHECK(!primordial_pipes.empty());
+          DCHECK_EQ(primordial_pipes.size(), 1u);
           primordial_pipes[0] = OutgoingInvitation::SendIsolated(
               std::move(channel_endpoint), {}, child_process_.Handle());
         }
@@ -199,8 +197,8 @@
                                    child_process_.Handle(),
                                    std::move(server_endpoint), error_callback);
         } else {
-          DCHECK(primordial_pipes);
-          DCHECK_EQ(num_primordial_pipes, 1u);
+          DCHECK(!primordial_pipes.empty());
+          DCHECK_EQ(primordial_pipes.size(), 1u);
           // Provide the remote process handle when calling SendIsolated
           // function.
           primordial_pipes[0] = OutgoingInvitation::SendIsolated(
@@ -213,8 +211,8 @@
           OutgoingInvitation::Send(std::move(invitation), {},
                                    std::move(server_endpoint), error_callback);
         } else {
-          DCHECK(primordial_pipes);
-          DCHECK_EQ(num_primordial_pipes, 1u);
+          DCHECK(!primordial_pipes.empty());
+          DCHECK_EQ(primordial_pipes.size(), 1u);
           // Don't provide the remote process handle when calling SendIsolated
           // function.
           primordial_pipes[0] =
@@ -314,8 +312,8 @@
 
 TEST_P(MAYBE_InvitationCppTest, Send) {
   ScopedMessagePipeHandle pipe;
-  LaunchChildTestClient("CppSendClient", &pipe, 1, InvitationType::kNormal,
-                        GetParam());
+  LaunchChildTestClient("CppSendClient", base::span_from_ref(pipe),
+                        InvitationType::kNormal, GetParam());
   WriteMessage(pipe, kTestMessage1);
   WaitForChildExit();
 }
@@ -328,7 +326,7 @@
 
 TEST_P(MAYBE_InvitationCppTest, SendIsolated) {
   ScopedMessagePipeHandle pipe;
-  LaunchChildTestClient("CppSendIsolatedClient", &pipe, 1,
+  LaunchChildTestClient("CppSendIsolatedClient", base::span_from_ref(pipe),
                         InvitationType::kIsolated, GetParam());
   WriteMessage(pipe, kTestMessage1);
   WaitForChildExit();
@@ -342,7 +340,7 @@
 #if BUILDFLAG(IS_WIN)
 TEST_P(MAYBE_InvitationCppTest, SendElevated) {
   ScopedMessagePipeHandle pipe;
-  LaunchChildTestClient("CppSendElevatedClient", &pipe, 1,
+  LaunchChildTestClient("CppSendElevatedClient", base::span_from_ref(pipe),
                         InvitationType::kElevated, GetParam());
   WriteMessage(pipe, kTestMessage1);
   WaitForChildExit();
@@ -357,7 +355,7 @@
 
 TEST_P(MAYBE_InvitationCppTest, SendWithMultiplePipes) {
   ScopedMessagePipeHandle pipes[2];
-  LaunchChildTestClient("CppSendWithMultiplePipesClient", pipes, 2,
+  LaunchChildTestClient("CppSendWithMultiplePipesClient", pipes,
                         InvitationType::kNormal, GetParam());
   WriteMessage(pipes[0], kTestMessage1);
   WriteMessage(pipes[1], kTestMessage2);
@@ -407,7 +405,8 @@
 
   ScopedMessagePipeHandle pipe;
   LaunchChildTestClient(
-      "CppProcessErrorsClient", &pipe, 1, InvitationType::kNormal, GetParam(),
+      "CppProcessErrorsClient", base::span_from_ref(pipe),
+      InvitationType::kNormal, GetParam(),
       base::BindLambdaForTesting([&](const std::string& error_message) {
         ASSERT_TRUE(actual_error_callback);
         actual_error_callback.Run(error_message);
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 4601a05..ced3a878 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1829,6 +1829,8 @@
       "disk_cache/sql/sql_entry_impl.h",
       "disk_cache/sql/sql_persistent_store.cc",
       "disk_cache/sql/sql_persistent_store.h",
+      "disk_cache/sql/sql_persistent_store_in_memory_index.cc",
+      "disk_cache/sql/sql_persistent_store_in_memory_index.h",
       "disk_cache/sql/sql_persistent_store_queries.h",
     ]
     deps += [
@@ -3512,6 +3514,7 @@
       "disk_cache/sql/exclusive_operation_coordinator_unittest.cc",
       "disk_cache/sql/indexed_pair_set_unittest.cc",
       "disk_cache/sql/sql_backend_impl_unittest.cc",
+      "disk_cache/sql/sql_persistent_store_in_memory_index_unittest.cc",
       "disk_cache/sql/sql_persistent_store_unittest.cc",
     ]
 
diff --git a/net/base/features.cc b/net/base/features.cc
index 5e0383a..b64efc1 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -222,8 +222,6 @@
 // by the top level site to reduce fingerprinting.
 BASE_FEATURE(kThirdPartyStoragePartitioning, base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kTopLevelTpcdOriginTrial, base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kTpcdTrialSettings,
              "TpcdSupportSettings",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/net/base/features.h b/net/base/features.h
index bf3a5c1..0f102109 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -300,9 +300,6 @@
 
 NET_EXPORT BASE_DECLARE_FEATURE(kThirdPartyStoragePartitioning);
 
-// Controls consideration of top-level 3PCD origin trial settings.
-NET_EXPORT BASE_DECLARE_FEATURE(kTopLevelTpcdOriginTrial);
-
 // Feature to enable consideration of 3PC deprecation trial settings.
 NET_EXPORT BASE_DECLARE_FEATURE(kTpcdTrialSettings);
 
diff --git a/net/cookies/cookie_util.cc b/net/cookies/cookie_util.cc
index 4594235..79c7e45 100644
--- a/net/cookies/cookie_util.cc
+++ b/net/cookies/cookie_util.cc
@@ -346,18 +346,17 @@
   UMA_HISTOGRAM_ENUMERATION("API.StorageAccess.AllowedRequests4", result);
 }
 
-bool DomainIsHostOnly(const std::string& domain_string) {
+bool DomainIsHostOnly(std::string_view domain_string) {
   return (domain_string.empty() || domain_string[0] != '.');
 }
 
-std::string CookieDomainAsHost(const std::string& cookie_domain) {
+std::string CookieDomainAsHost(std::string_view cookie_domain) {
   if (DomainIsHostOnly(cookie_domain))
-    return cookie_domain;
-  return cookie_domain.substr(1);
+    return std::string(cookie_domain);
+  return std::string(cookie_domain.substr(1));
 }
 
-std::string GetEffectiveDomain(const std::string& scheme,
-                               const std::string& host) {
+std::string GetEffectiveDomain(std::string_view scheme, std::string_view host) {
   if (scheme == "http" || scheme == "https" || scheme == "ws" ||
       scheme == "wss") {
     return registry_controlled_domains::GetDomainAndRegistry(
@@ -647,9 +646,9 @@
   return url_path.substr(0, idx);
 }
 
-GURL CookieDomainAndPathToURL(const std::string& domain,
-                              const std::string& path,
-                              const std::string& source_scheme) {
+GURL CookieDomainAndPathToURL(std::string_view domain,
+                              std::string_view path,
+                              std::string_view source_scheme) {
   // Note: domain_no_dot could be empty for e.g. file cookies.
   std::string domain_no_dot = CookieDomainAsHost(domain);
   if (domain_no_dot.empty() || source_scheme.empty())
@@ -658,27 +657,27 @@
       {source_scheme, url::kStandardSchemeSeparator, domain_no_dot, path}));
 }
 
-GURL CookieDomainAndPathToURL(const std::string& domain,
-                              const std::string& path,
+GURL CookieDomainAndPathToURL(std::string_view domain,
+                              std::string_view path,
                               bool is_https) {
   return CookieDomainAndPathToURL(
       domain, path,
       std::string(is_https ? url::kHttpsScheme : url::kHttpScheme));
 }
 
-GURL CookieDomainAndPathToURL(const std::string& domain,
-                              const std::string& path,
+GURL CookieDomainAndPathToURL(std::string_view domain,
+                              std::string_view path,
                               CookieSourceScheme source_scheme) {
   return CookieDomainAndPathToURL(domain, path,
                                   source_scheme == CookieSourceScheme::kSecure);
 }
 
-GURL CookieOriginToURL(const std::string& domain, bool is_https) {
+GURL CookieOriginToURL(std::string_view domain, bool is_https) {
   return CookieDomainAndPathToURL(domain, "/", is_https);
 }
 
 GURL SimulatedCookieSource(const CanonicalCookie& cookie,
-                           const std::string& source_scheme) {
+                           std::string_view source_scheme) {
   return CookieDomainAndPathToURL(cookie.Domain(), cookie.Path(),
                                   source_scheme);
 }
@@ -759,7 +758,7 @@
   return true;
 }
 
-CookiePrefix GetCookiePrefix(const std::string& name) {
+CookiePrefix GetCookiePrefix(std::string_view name) {
   constexpr std::string_view kSecurePrefix("__Secure-");
   constexpr std::string_view kHostPrefix("__Host-");
   constexpr std::string_view kHttpPrefix("__Http-");
@@ -840,7 +839,7 @@
   return result;
 }
 
-void ParseRequestCookieLine(const std::string& header_value,
+void ParseRequestCookieLine(std::string_view header_value,
                             ParsedRequestCookies* parsed_cookies) {
   std::string::const_iterator i = header_value.begin();
   while (i != header_value.end()) {
@@ -895,7 +894,7 @@
 }
 
 CookieOptions::SameSiteCookieContext ComputeSameSiteContextForRequest(
-    const std::string& http_method,
+    std::string_view http_method,
     const std::vector<GURL>& url_chain,
     const SiteForCookies& site_for_cookies,
     const std::optional<url::Origin>& initiator,
@@ -1104,7 +1103,7 @@
 }
 
 CookieOptions::SameSiteCookieContext::ContextMetadata::HttpMethod
-HttpMethodStringToEnum(const std::string& in) {
+HttpMethodStringToEnum(std::string_view in) {
   using HttpMethod =
       CookieOptions::SameSiteCookieContext::ContextMetadata::HttpMethod;
   if (in == "GET")
diff --git a/net/cookies/cookie_util.h b/net/cookies/cookie_util.h
index a8e1ac3..011e26e 100644
--- a/net/cookies/cookie_util.h
+++ b/net/cookies/cookie_util.h
@@ -173,8 +173,8 @@
 // Returns the effective TLD+1 for a given host. This only makes sense for http
 // and https schemes. For other schemes, the host will be returned unchanged
 // (minus any leading period).
-NET_EXPORT std::string GetEffectiveDomain(const std::string& scheme,
-                                          const std::string& host);
+NET_EXPORT std::string GetEffectiveDomain(std::string_view scheme,
+                                          std::string_view host);
 
 // Determine the actual cookie domain based on the domain string passed
 // (if any) and the URL from which the cookie came.
@@ -190,14 +190,14 @@
 
 // Returns true if a domain string represents a host-only cookie,
 // i.e. it doesn't begin with a leading '.' character.
-NET_EXPORT bool DomainIsHostOnly(const std::string& domain_string);
+NET_EXPORT bool DomainIsHostOnly(std::string_view domain_string);
 
 // If |cookie_domain| is nonempty and starts with a "." character, this returns
 // the substring of |cookie_domain| without the leading dot. (Note only one
 // leading dot is stripped, if there are multiple.) Otherwise it returns
 // |cookie_domain|. This is useful for converting from CanonicalCookie's
 // representation of a cookie domain to the RFC's notion of a cookie's domain.
-NET_EXPORT std::string CookieDomainAsHost(const std::string& cookie_domain);
+NET_EXPORT std::string CookieDomainAsHost(std::string_view cookie_domain);
 
 // Parses the string with the cookie expiration time (very forgivingly).
 // Returns the "null" time on failure.
@@ -219,25 +219,25 @@
 // attribute per RFC6265bis. The GURL is constructed after stripping off any
 // leading dot.
 // Note: the GURL returned by this method is not guaranteed to be valid.
-NET_EXPORT GURL CookieDomainAndPathToURL(const std::string& domain,
-                                         const std::string& path,
-                                         const std::string& source_scheme);
-NET_EXPORT GURL CookieDomainAndPathToURL(const std::string& domain,
-                                         const std::string& path,
+NET_EXPORT GURL CookieDomainAndPathToURL(std::string_view domain,
+                                         std::string_view path,
+                                         std::string_view source_scheme);
+NET_EXPORT GURL CookieDomainAndPathToURL(std::string_view domain,
+                                         std::string_view path,
                                          bool is_https);
-NET_EXPORT GURL CookieDomainAndPathToURL(const std::string& domain,
-                                         const std::string& path,
+NET_EXPORT GURL CookieDomainAndPathToURL(std::string_view domain,
+                                         std::string_view path,
                                          CookieSourceScheme source_scheme);
 
 // Convenience for converting a cookie origin (domain and https pair) to a URL.
-NET_EXPORT GURL CookieOriginToURL(const std::string& domain, bool is_https);
+NET_EXPORT GURL CookieOriginToURL(std::string_view domain, bool is_https);
 
 // Returns a URL that could have been the cookie's source.
 // Not guaranteed to actually be the URL that set the cookie. Not guaranteed to
 // be a valid GURL. Intended as a shim for SetCanonicalCookieAsync calls, where
 // a source URL is required but only a source scheme may be available.
 NET_EXPORT GURL SimulatedCookieSource(const CanonicalCookie& cookie,
-                                      const std::string& source_scheme);
+                                      std::string_view source_scheme);
 
 // Provisional evaluation of acceptability of setting secure cookies on
 // `source_url` based only on the `source_url`'s scheme and whether it
@@ -262,7 +262,7 @@
 
 // Returns the CookiePrefix (or COOKIE_PREFIX_NONE if none) that
 // applies to the given cookie |name|.
-CookiePrefix GetCookiePrefix(const std::string& name);
+CookiePrefix GetCookiePrefix(std::string_view name);
 
 // Returns true if the cookie does not violate any constraints imposed
 // by the cookie name's prefix, as described in
@@ -301,7 +301,7 @@
 // these will appear in |parsed_cookies| as well. The cookie header can be
 // written by non-Chromium consumers (such as extensions), so the header may not
 // be well-formed.
-NET_EXPORT void ParseRequestCookieLine(const std::string& header_value,
+NET_EXPORT void ParseRequestCookieLine(std::string_view header_value,
                                        ParsedRequestCookies* parsed_cookies);
 
 // Writes all cookies of |parsed_cookies| into a HTTP Request header value
@@ -350,7 +350,7 @@
 // lax same-site but not strict same-site, SameSite=lax cookies be only sent
 // when the method is "safe" in the RFC7231 section 4.2.1 sense.
 NET_EXPORT CookieOptions::SameSiteCookieContext
-ComputeSameSiteContextForRequest(const std::string& http_method,
+ComputeSameSiteContextForRequest(std::string_view http_method,
                                  const std::vector<GURL>& url_chain,
                                  const SiteForCookies& site_for_cookies,
                                  const std::optional<url::Origin>& initiator,
@@ -432,7 +432,7 @@
 // Converts a string representing the http request method to its enum
 // representation.
 NET_EXPORT CookieOptions::SameSiteCookieContext::ContextMetadata::HttpMethod
-HttpMethodStringToEnum(const std::string& in);
+HttpMethodStringToEnum(std::string_view in);
 
 // Takes a CookieAccessResult and returns a bool, returning true if the
 // CookieInclusionStatus in CookieAccessResult was set to "include", else
diff --git a/net/data/ssl/chrome_root_store/root_store.md b/net/data/ssl/chrome_root_store/root_store.md
index 92c4766..970ca9a0 100644
--- a/net/data/ssl/chrome_root_store/root_store.md
+++ b/net/data/ssl/chrome_root_store/root_store.md
@@ -1,7 +1,7 @@
 <!-- mdformat off(generated) -->
 <!-- mdlint off(generated) -->
 # Chrome Root Store
-Version: 24
+Version: 25
 
 [TOC]
 
diff --git a/net/data/ssl/chrome_root_store/root_store.textproto b/net/data/ssl/chrome_root_store/root_store.textproto
index 687eec0..1d7bdbc 100644
--- a/net/data/ssl/chrome_root_store/root_store.textproto
+++ b/net/data/ssl/chrome_root_store/root_store.textproto
@@ -8,7 +8,7 @@
 
 # Version # should always be incremented up whenever this (or any pem file that
 # it references) is changed.
-version_major: 24
+version_major: 25
 
 # CN=Actalis Authentication Root CA, O=Actalis S.p.A./03358520967, L=Milan, C=IT
 # https://ssltest-a.actalis.it:8443
@@ -394,6 +394,7 @@
 # CN=ISRG Root X1, O=Internet Security Research Group, C=US
 trust_anchors {
   sha256_hex: "96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6"
+  trust_anchor_id: "\x82\xdf\x13\x02\x01"  # 44947.2.1
 }
 
 # CN=Izenpe.com, O=IZENPE S.A., C=ES
@@ -664,6 +665,7 @@
 # CN=ISRG Root X2, O=Internet Security Research Group, C=US
 trust_anchors {
   sha256_hex: "69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470"
+  trust_anchor_id: "\x82\xdf\x13\x02\x06"  # 44947.2.6
 }
 
 # CN=NAVER Global Root Certification Authority, O=NAVER BUSINESS PLATFORM Corp., C=KR
diff --git a/net/disk_cache/sql/sql_backend_constants.h b/net/disk_cache/sql/sql_backend_constants.h
index 60e8a14..2644e4a0 100644
--- a/net/disk_cache/sql/sql_backend_constants.h
+++ b/net/disk_cache/sql/sql_backend_constants.h
@@ -51,21 +51,28 @@
 //            index on `(cache_key_hash, doomed)` to the `resources` table.
 // Version 3: https://crrev.com/c/6940353 replaced `(token_high, token_low)`
 //            with `res_id` in `resources` and `blobs` tables.
+// Version 4: https://crrev.com/c/7005549 changed the eviction logic to use
+//            `res_id` instead of `cache_key` and added a covering index on
+//            `(last_used, bytes_usage)` to the `resources` table.
+// Version 5: https://crrev.com/c/7005917 changed how doomed entries are
+//            cleaned up. Instead of a delayed task, cleanup is now triggered
+//            during browser idle periods. Also, the index on `res_id` for
+//            doomed entries was removed as it's no longer needed.
 // ----------------------------------------------------------------------------
 
 // The oldest database schema version that the current code can read.
 // A database with a version older than this will be razed as it's considered
 // obsolete and the code no longer supports migrating from it.
-inline constexpr int kSqlBackendLowestSupportedDatabaseVersion = 3;
+inline constexpr int kSqlBackendLowestSupportedDatabaseVersion = 5;
 
 // The current version of the database schema. This should be incremented for
 // any schema change.
-inline constexpr int kSqlBackendCurrentDatabaseVersion = 3;
+inline constexpr int kSqlBackendCurrentDatabaseVersion = 5;
 
 // The oldest application version that can use a database with the current
 // schema. If a schema change is not backward-compatible, this must be set to
 // the same value as `kSqlBackendCurrentDatabaseVersion`.
-inline constexpr int kSqlBackendCompatibleDatabaseVersion = 3;
+inline constexpr int kSqlBackendCompatibleDatabaseVersion = 5;
 
 // Estimated static size overhead for a resource entry in the database,
 // excluding the key and any blob data. This is a conservative estimate based on
diff --git a/net/disk_cache/sql/sql_backend_impl.cc b/net/disk_cache/sql/sql_backend_impl.cc
index 275bc706..e5f4b37 100644
--- a/net/disk_cache/sql/sql_backend_impl.cc
+++ b/net/disk_cache/sql/sql_backend_impl.cc
@@ -375,15 +375,6 @@
                                    const std::vector<bool>& results) {
   const bool success = std::all_of(results.begin(), results.end(),
                                    [](bool result) { return result; });
-  if (success) {
-    // Schedule a one-time task to clean up doomed entries from previous
-    // sessions. This runs after a delay to avoid impacting startup performance.
-    base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(&SqlBackendImpl::TriggerDeleteDoomedEntries,
-                       weak_factory_.GetWeakPtr()),
-        kSqlBackendDeleteDoomedEntriesDelay);
-  }
   std::move(callback).Run(success ? net::OK : net::ERR_FAILED);
 }
 
@@ -653,22 +644,26 @@
     return;
   }
 
-  // Collect keys of active entries to exclude them from the store's
+  // Collect Ids of active entries to exclude them from the store's
   // DeleteLiveEntriesBetween operation, as they will be handled by dooming them
   // directly within this method.
-  std::vector<CacheEntryKey> excluded_keys_vec;
-  excluded_keys_vec.reserve(active_entries_.size());
+  std::vector<SqlPersistentStore::ResId> excluded_ids_vec;
+  excluded_ids_vec.reserve(active_entries_.size());
   std::vector<SqlEntryImpl*> active_entries_to_be_doomed;
   for (auto& it : active_entries_) {
-    excluded_keys_vec.push_back(it.first);
+    const auto optional_res_id = GetResId(it.second->res_id_or_error());
+    if (optional_res_id.has_value()) {
+      excluded_ids_vec.push_back(*optional_res_id);
+    }
     // Check if the active entry falls within the specified time range.
     const base::Time last_used_time = it.second->GetLastUsed();
     if (last_used_time >= initial_time && last_used_time < end_time) {
       active_entries_to_be_doomed.push_back(&it.second.get());
     }
   }
-  base::flat_set<CacheEntryKey> excluded_keys(base::sorted_unique,
-                                              std::move(excluded_keys_vec));
+  std::sort(excluded_ids_vec.begin(), excluded_ids_vec.end());
+  base::flat_set<SqlPersistentStore::ResId> excluded_ids(
+      base::sorted_unique, std::move(excluded_ids_vec));
 
   auto barrier_callback = base::BarrierCallback<int>(
       active_entries_to_be_doomed.size() +  // For active entries being doomed
@@ -695,7 +690,7 @@
   // pending) entries within the specified time range, excluding those already
   // handled.
   store_->DeleteLiveEntriesBetween(
-      initial_time, end_time, std::move(excluded_keys),
+      initial_time, end_time, std::move(excluded_ids),
       base::BindOnce(
           [](CompletionOnceCallback callback,
              SqlPersistentStore::Error result) {
@@ -789,6 +784,7 @@
 }
 
 void SqlBackendImpl::OnBrowserIdle() {
+  store_->MaybeRunCleanupDoomedEntries(base::DoNothing());
   store_->MaybeRunCheckpoint(base::DoNothing());
 }
 
@@ -1346,34 +1342,10 @@
   if (!store_->ShouldStartEviction()) {
     return;
   }
-  std::vector<CacheEntryKey> excluded_keys_vec;
-  excluded_keys_vec.reserve(active_entries_.size());
-  for (const auto& pair : active_entries_) {
-    excluded_keys_vec.push_back(pair.first);
-  }
-  base::flat_set<CacheEntryKey> excluded_keys(base::sorted_unique,
-                                              std::move(excluded_keys_vec));
-  store_->StartEviction(
-      std::move(excluded_keys),
-      base::BindOnce([](SqlPersistentStore::Error result) {}));
-}
-
-void SqlBackendImpl::TriggerDeleteDoomedEntries() {
-  // TODO(crbug.com/443171275): Get information on whether a doomed entry
-  // exists when initializing SqlPersistentStore, and if it does not exist, do
-  // not execute TriggerDeleteDoomedEntries.
-  // TODO(crbug.com/443171275): Execute only when the browser is idle.
-  exclusive_operation_coordinator_.PostOrRunExclusiveOperation(base::BindOnce(
-      base::BindOnce(&SqlBackendImpl::HandleDeleteDoomedEntriesOperation,
-                     weak_factory_.GetWeakPtr())));
-}
-
-void SqlBackendImpl::HandleDeleteDoomedEntriesOperation(
-    std::unique_ptr<ExclusiveOperationCoordinator::OperationHandle> handle) {
   std::vector<SqlPersistentStore::ResId> excluded_ids_vec;
-  excluded_ids_vec.reserve(doomed_entries_.size());
-  for (const auto& entry : doomed_entries_) {
-    const auto optional_res_id = GetResId(entry->res_id_or_error());
+  excluded_ids_vec.reserve(active_entries_.size());
+  for (const auto& it : active_entries_) {
+    const auto optional_res_id = GetResId(it.second->res_id_or_error());
     if (optional_res_id.has_value()) {
       excluded_ids_vec.push_back(*optional_res_id);
     }
@@ -1381,10 +1353,9 @@
   std::sort(excluded_ids_vec.begin(), excluded_ids_vec.end());
   base::flat_set<SqlPersistentStore::ResId> excluded_ids(
       base::sorted_unique, std::move(excluded_ids_vec));
-  store_->DeleteDoomedEntries(
+  store_->StartEviction(
       std::move(excluded_ids),
-      base::BindOnce([](SqlPersistentStore::Error result) {
-      }).Then(OnceClosureWithBoundArgs(std::move(handle))));
+      base::BindOnce([](SqlPersistentStore::Error result) {}));
 }
 
 void SqlBackendImpl::EnableStrictCorruptionCheckForTesting() {
diff --git a/net/disk_cache/sql/sql_backend_impl.h b/net/disk_cache/sql/sql_backend_impl.h
index a7bbec59..311c639 100644
--- a/net/disk_cache/sql/sql_backend_impl.h
+++ b/net/disk_cache/sql/sql_backend_impl.h
@@ -472,18 +472,6 @@
       const CacheEntryKey& key,
       SqlPersistentStore::EntryInfo& entry_info);
 
-  // Schedules the `HandleDeleteDoomedEntriesOperation` task to run. This is the
-  // entry point for the one-time cleanup of entries that were doomed in a
-  // previous session.
-  void TriggerDeleteDoomedEntries();
-
-  // Physically deletes entries that were marked as "doomed" in previous
-  // sessions from the database. It excludes any currently active doomed entries
-  // to prevent data corruption. This method is executed as an exclusive
-  // operation to ensure it has sole access to the cache during cleanup.
-  void HandleDeleteDoomedEntriesOperation(
-      std::unique_ptr<ExclusiveOperationCoordinator::OperationHandle> handle);
-
   const base::FilePath path_;
 
   // Task runner for all background SQLite operations.
diff --git a/net/disk_cache/sql/sql_backend_impl_unittest.cc b/net/disk_cache/sql/sql_backend_impl_unittest.cc
index 1baa4ba0..54c0829 100644
--- a/net/disk_cache/sql/sql_backend_impl_unittest.cc
+++ b/net/disk_cache/sql/sql_backend_impl_unittest.cc
@@ -1235,8 +1235,13 @@
   entry2->Doom();
 
   base::HistogramTester histogram_tester;
-  task_environment_.FastForwardBy(kSqlBackendDeleteDoomedEntriesDelay +
-                                  base::Seconds(1));
+  backend->OnBrowserIdle();
+
+  // Flush the queue to ensure that cleanup task is completed.
+  net::TestCompletionCallback flush_cb;
+  backend->FlushQueueForTest(flush_cb.callback());
+  EXPECT_THAT(flush_cb.WaitForResult(), IsOk());
+
   // Verify that `DeleteDoomedEntriesCount` UMA was recorded in the histogram.
   histogram_tester.ExpectUniqueSample(
       "Net.SqlDiskCache.DeleteDoomedEntriesCount", 1, 1);
diff --git a/net/disk_cache/sql/sql_persistent_store.cc b/net/disk_cache/sql/sql_persistent_store.cc
index 2bdc308..44fb843b 100644
--- a/net/disk_cache/sql/sql_persistent_store.cc
+++ b/net/disk_cache/sql/sql_persistent_store.cc
@@ -29,6 +29,7 @@
 #include "net/disk_cache/cache_util.h"
 #include "net/disk_cache/sql/indexed_pair_set.h"
 #include "net/disk_cache/sql/sql_backend_constants.h"
+#include "net/disk_cache/sql/sql_persistent_store_in_memory_index.h"
 #include "net/disk_cache/sql/sql_persistent_store_queries.h"
 #include "sql/database.h"
 #include "sql/error_delegate_util.h"
@@ -57,9 +58,6 @@
 };
 // LINT.ThenChange(//tools/metrics/histograms/metadata/net/enums.xml:SqlDiskCacheIndexMismatchLocation)
 
-using HashResIdSet =
-    IndexedPairSet<CacheEntryKey::Hash, SqlPersistentStore::ResId>;
-
 // Holds summary statistics about the cache store.
 struct StoreStatus {
   int32_t entry_count = 0;
@@ -68,14 +66,19 @@
 
 // The result of a successful initialization.
 struct InitResult {
-  InitResult(int64_t max_bytes, HashResIdSet&& index)
-      : max_bytes(max_bytes), index(std::move(index)) {}
+  InitResult(int64_t max_bytes,
+             SqlPersistentStoreInMemoryIndex&& index,
+             std::vector<SqlPersistentStore::ResId> doomed_entry_res_ids)
+      : max_bytes(max_bytes),
+        index(std::move(index)),
+        doomed_entry_res_ids(std::move(doomed_entry_res_ids)) {}
   ~InitResult() = default;
   InitResult(InitResult&& other) = default;
   InitResult& operator=(InitResult&& other) = default;
 
   int64_t max_bytes = 0;
-  HashResIdSet index;
+  SqlPersistentStoreInMemoryIndex index;
+  std::vector<SqlPersistentStore::ResId> doomed_entry_res_ids;
 };
 
 // A helper struct to associate an IOBuffer with a starting offset.
@@ -84,19 +87,6 @@
   int64_t start;
 };
 
-// A helper struct to hold a resource ID and a cache entry key hash.
-struct ResIdAndHashKey {
-  ResIdAndHashKey(SqlPersistentStore::ResId res_id,
-                  CacheEntryKey::Hash key_hash)
-      : res_id(res_id), key_hash(key_hash) {}
-  ~ResIdAndHashKey() = default;
-  ResIdAndHashKey(ResIdAndHashKey&&) = default;
-  ResIdAndHashKey& operator=(ResIdAndHashKey&&) = default;
-
-  SqlPersistentStore::ResId res_id;
-  CacheEntryKey::Hash key_hash;
-};
-
 using disk_cache_sql_queries::GetQuery;
 using disk_cache_sql_queries::Query;
 
@@ -110,8 +100,8 @@
     SqlPersistentStore::OptionalEntryInfoWithIdAndKey;
 using IntOrError = SqlPersistentStore::IntOrError;
 using InitResultOrError = base::expected<InitResult, Error>;
-using ResIdAndHashKeyList = std::vector<ResIdAndHashKey>;
-using ResIdAndHashKeyListOrError = base::expected<ResIdAndHashKeyList, Error>;
+using ResIdList = std::vector<ResId>;
+using ResIdListOrError = base::expected<ResIdList, Error>;
 
 // A helper struct to bundle an operation's result with a flag indicating
 // whether an eviction check is needed. This allows the background sequence,
@@ -137,8 +127,8 @@
 using EntryInfoOrErrorAndEvictionRequested =
     ResultAndEvictionRequested<EntryInfoOrError>;
 using IntOrErrorAndEvictionRequested = ResultAndEvictionRequested<IntOrError>;
-using ResIdAndHashKeyListOrErrorAndEvictionRequested =
-    ResultAndEvictionRequested<ResIdAndHashKeyListOrError>;
+using ResIdListOrErrorAndEvictionRequested =
+    ResultAndEvictionRequested<ResIdListOrError>;
 
 bool IsBlobSizeValid(int64_t blob_start,
                      int64_t blob_end,
@@ -205,7 +195,7 @@
     dict.Add("entry_info", "not found");
   }
 }
-void PopulateTraceDetails(const ResIdAndHashKeyList& result,
+void PopulateTraceDetails(const ResIdList& result,
                           perfetto::TracedDictionary& dict) {
   dict.Add("doomed_entry_count", result.size());
 }
@@ -262,7 +252,6 @@
       !db.Execute(GetQuery(Query::kInitSchema_CreateTableBlobs)) ||
       !db.Execute(GetQuery(Query::kIndex_ResourcesCacheKeyHashDoomed)) ||
       !db.Execute(GetQuery(Query::kIndex_LiveResourcesLastUsed)) ||
-      !db.Execute(GetQuery(Query::kIndex_DoomedResourcesResId)) ||
       !db.Execute(GetQuery(Query::kIndex_BlobsResIdStart))) {
     return false;
   }
@@ -356,17 +345,17 @@
   ErrorAndEvictionRequested DeleteDoomedEntry(const CacheEntryKey& key,
                                               ResId res_id,
                                               base::TimeTicks start_time);
-  Error DeleteDoomedEntries(base::flat_set<ResId> excluded_res_ids,
+  Error DeleteDoomedEntries(ResIdList res_ids_to_delete,
                             base::TimeTicks start_time);
-  ResIdAndHashKeyListOrErrorAndEvictionRequested DeleteLiveEntry(
+  ResIdListOrErrorAndEvictionRequested DeleteLiveEntry(
       const CacheEntryKey& key,
       base::TimeTicks start_time);
 
   ErrorAndEvictionRequested DeleteAllEntries(base::TimeTicks start_time);
-  ResIdAndHashKeyListOrErrorAndEvictionRequested DeleteLiveEntriesBetween(
+  ResIdListOrErrorAndEvictionRequested DeleteLiveEntriesBetween(
       base::Time initial_time,
       base::Time end_time,
-      base::flat_set<CacheEntryKey> excluded_keys,
+      base::flat_set<ResId> excluded_res_ids,
       base::TimeTicks start_time);
   Error UpdateEntryLastUsedByKey(const CacheEntryKey& key,
                                  base::Time last_used,
@@ -406,8 +395,8 @@
   OptionalEntryInfoWithIdAndKey OpenLatestEntryBeforeResId(
       ResId res_id_cursor,
       base::TimeTicks start_time);
-  ResIdAndHashKeyListOrErrorAndEvictionRequested RunEviction(
-      base::flat_set<CacheEntryKey> excluded_keys,
+  ResIdListOrErrorAndEvictionRequested RunEviction(
+      base::flat_set<ResId> excluded_res_ids,
       base::TimeTicks start_time);
   bool MaybeRunCheckpoint();
 
@@ -422,7 +411,9 @@
  private:
   void DatabaseErrorCallback(int error, sql::Statement* statement);
 
-  Error InitializeInternal(bool& corruption_detected, HashResIdSet& index);
+  Error InitializeInternal(bool& corruption_detected,
+                           SqlPersistentStoreInMemoryIndex& index,
+                           ResIdList& doomed_entry_res_ids);
   EntryInfoOrError OpenOrCreateEntryInternal(const CacheEntryKey& key,
                                              bool& corruption_detected);
   OptionalEntryInfoOrError OpenEntryInternal(const CacheEntryKey& key);
@@ -432,17 +423,15 @@
                                        bool& corruption_detected);
   Error DoomEntryInternal(ResId res_id, bool& corruption_detected);
   Error DeleteDoomedEntryInternal(ResId res_id);
-  Error DeleteDoomedEntriesInternal(
-      const base::flat_set<ResId>& excluded_res_ids,
-      size_t& deleted_count,
-      bool& corruption_detected);
-  ResIdAndHashKeyListOrError DeleteLiveEntryInternal(const CacheEntryKey& key,
-                                                     bool& corruption_detected);
+  Error DeleteDoomedEntriesInternal(const ResIdList& res_ids_to_delete,
+                                    bool& corruption_detected);
+  ResIdListOrError DeleteLiveEntryInternal(const CacheEntryKey& key,
+                                           bool& corruption_detected);
   Error DeleteAllEntriesInternal(bool& corruption_detected);
-  ResIdAndHashKeyListOrError DeleteLiveEntriesBetweenInternal(
+  ResIdListOrError DeleteLiveEntriesBetweenInternal(
       base::Time initial_time,
       base::Time end_time,
-      const base::flat_set<CacheEntryKey>& excluded_keys,
+      const base::flat_set<ResId>& excluded_res_ids,
       bool& corruption_detected);
   Error UpdateEntryLastUsedByKeyInternal(const CacheEntryKey& key,
                                          base::Time last_used);
@@ -476,8 +465,8 @@
   OptionalEntryInfoWithIdAndKey OpenLatestEntryBeforeResIdInternal(
       ResId res_id_cursor,
       bool& corruption_detected);
-  ResIdAndHashKeyListOrError RunEvictionInternal(
-      const base::flat_set<CacheEntryKey>& excluded_keys,
+  ResIdListOrError RunEvictionInternal(
+      const base::flat_set<ResId>& excluded_res_ids,
       bool& corruption_detected);
 
   // Trims blobs that overlap with the new write range [offset, end), and
@@ -582,8 +571,10 @@
   base::ElapsedTimer timer;
   CHECK(!db_init_status_.has_value());
   bool corruption_detected = false;
-  HashResIdSet index;
-  db_init_status_ = InitializeInternal(corruption_detected, index);
+  SqlPersistentStoreInMemoryIndex index;
+  ResIdList doomed_entry_res_ids;
+  db_init_status_ =
+      InitializeInternal(corruption_detected, index, doomed_entry_res_ids);
   RecordTimeAndErrorResultHistogram("Initialize", posting_delay,
                                     timer.Elapsed(), *db_init_status_,
                                     corruption_detected);
@@ -611,12 +602,14 @@
   }
 
   return *db_init_status_ == Error::kOk
-             ? InitResultOrError(InitResult(max_bytes_, std::move(index)))
+             ? InitResultOrError(InitResult(max_bytes_, std::move(index),
+                                            std::move(doomed_entry_res_ids)))
              : base::unexpected(*db_init_status_);
 }
 
 Error Backend::InitializeInternal(bool& corruption_detected,
-                                  HashResIdSet& index) {
+                                  SqlPersistentStoreInMemoryIndex& index,
+                                  ResIdList& doomed_entry_res_ids) {
   if (simulate_db_failure_for_testing_) {
     return Error::kFailedForTesting;
   }
@@ -676,9 +669,7 @@
       const auto key_hash = CacheEntryKey::Hash(statement.ColumnInt64(1));
       const bool doomed = statement.ColumnBool(2);
       if (doomed) {
-        // TODO(crbug.com/443171275): Return information to SqlBackendImpl that
-        // a doomed entry exists, and if no doomed entry exists, do not execute
-        // TriggerDeleteDoomedEntries.
+        doomed_entry_res_ids.emplace_back(res_id);
       } else {
         index.Insert(key_hash, res_id);
       }
@@ -1061,34 +1052,31 @@
   return transaction.Commit() ? Error::kOk : Error::kFailedToCommitTransaction;
 }
 
-Error Backend::DeleteDoomedEntries(base::flat_set<ResId> excluded_res_ids,
+Error Backend::DeleteDoomedEntries(ResIdList res_ids_to_delete,
                                    base::TimeTicks start_time) {
   const base::TimeDelta posting_delay = base::TimeTicks::Now() - start_time;
   TRACE_EVENT_BEGIN0("disk_cache", "SqlBackend.DeleteDoomedEntries");
   base::ElapsedTimer timer;
   bool corruption_detected = false;
-  size_t deleted_count = 0;
-  auto result = DeleteDoomedEntriesInternal(excluded_res_ids, deleted_count,
-                                            corruption_detected);
+  auto result =
+      DeleteDoomedEntriesInternal(res_ids_to_delete, corruption_detected);
   RecordTimeAndErrorResultHistogram("DeleteDoomedEntries", posting_delay,
                                     timer.Elapsed(), result,
                                     corruption_detected);
   base::UmaHistogramCounts100("Net.SqlDiskCache.DeleteDoomedEntriesCount",
-                              deleted_count);
+                              res_ids_to_delete.size());
   TRACE_EVENT_END1("disk_cache", "SqlBackend.DeleteDoomedEntries", "result",
                    [&](perfetto::TracedValue trace_context) {
                      auto dict = std::move(trace_context).WriteDictionary();
                      PopulateTraceDetails(result, store_status_, dict);
-                     dict.Add("deleted_count", deleted_count);
+                     dict.Add("deleted_count", res_ids_to_delete.size());
                    });
   MaybeCrashIfCorrupted(corruption_detected);
   return result;
 }
 
-Error Backend::DeleteDoomedEntriesInternal(
-    const base::flat_set<ResId>& excluded_res_ids,
-    size_t& deleted_count,
-    bool& corruption_detected) {
+Error Backend::DeleteDoomedEntriesInternal(const ResIdList& res_ids_to_delete,
+                                           bool& corruption_detected) {
   if (simulate_db_failure_for_testing_) {
     return Error::kFailedForTesting;
   }
@@ -1098,46 +1086,25 @@
     return Error::kFailedToStartTransaction;
   }
 
-  std::vector<ResId> res_ids_to_delete;
-
-  // 1. Select all doomed entries.
-  {
-    sql::Statement statement(db_.GetCachedStatement(
-        SQL_FROM_HERE,
-        GetQuery(Query::kDeleteDoomedEntries_SelectDoomedResources)));
-    // 2. Collect entries to be deleted, skipping excluded ones.
-    while (statement.Step()) {
-      ResId res_id(statement.ColumnInt64(0));
-      if (excluded_res_ids.contains(res_id)) {
-        continue;
-      }
-      res_ids_to_delete.push_back(res_id);
-    }
-  }
-
-  deleted_count = res_ids_to_delete.size();
-  if (deleted_count == 0) {
-    // Nothing to delete, abort the transaction and return kOk;
-    return Error::kOk;
-  }
-
-  // 3. Delete from `resources` table by `res_id`.
+  // 1. Delete from `resources` table by `res_id`.
   if (auto error = DeleteResourcesByResIds(res_ids_to_delete);
       error != Error::kOk) {
     return error;
   }
 
-  // 4. Delete corresponding blobs by res_id.
+  // 2. Delete corresponding blobs by res_id.
   if (auto error = DeleteBlobsByResIds(res_ids_to_delete);
       error != Error::kOk) {
     return error;
   }
 
-  // 5. Commit the transaction.
+  // 3. Commit the transaction.
+  // Note: The entries for the res IDs passed to this method are assumed to be
+  // doomed, so store_status_'s entry_count and total_size are not updated.
   return transaction.Commit() ? Error::kOk : Error::kFailedToCommitTransaction;
 }
 
-ResIdAndHashKeyListOrErrorAndEvictionRequested Backend::DeleteLiveEntry(
+ResIdListOrErrorAndEvictionRequested Backend::DeleteLiveEntry(
     const CacheEntryKey& key,
     base::TimeTicks start_time) {
   const base::TimeDelta posting_delay = base::TimeTicks::Now() - start_time;
@@ -1160,13 +1127,12 @@
                      dict.Add("corruption_detected", corruption_detected);
                    });
   MaybeCrashIfCorrupted(corruption_detected);
-  return ResIdAndHashKeyListOrErrorAndEvictionRequested(std::move(result),
-                                                        ShouldStartEviction());
+  return ResIdListOrErrorAndEvictionRequested(std::move(result),
+                                              ShouldStartEviction());
 }
 
-ResIdAndHashKeyListOrError Backend::DeleteLiveEntryInternal(
-    const CacheEntryKey& key,
-    bool& corruption_detected) {
+ResIdListOrError Backend::DeleteLiveEntryInternal(const CacheEntryKey& key,
+                                                  bool& corruption_detected) {
   if (simulate_db_failure_for_testing_) {
     return base::unexpected(Error::kFailedForTesting);
   }
@@ -1178,10 +1144,9 @@
 
   // We need to collect the res_ids of deleted entries to later remove their
   // corresponding data from the `blobs` table.
-  std::vector<ResId> res_ids_to_be_deleted;
+  ResIdList res_ids_to_be_deleted;
   // Use checked numerics to safely update the total cache size.
   base::CheckedNumeric<int64_t> total_size_delta = 0;
-  ResIdAndHashKeyList deleted_enties;
   {
     sql::Statement statement(db_.GetCachedStatement(
         SQL_FROM_HERE, GetQuery(Query::kDeleteLiveEntry_DeleteFromResources)));
@@ -1190,7 +1155,6 @@
     while (statement.Step()) {
       const auto res_id = ResId(statement.ColumnInt64(0));
       res_ids_to_be_deleted.emplace_back(res_id);
-      deleted_enties.emplace_back(res_id, key.hash());
       // The size of the deleted entry is subtracted from the total.
       total_size_delta -= statement.ColumnInt64(1);
     }
@@ -1218,7 +1182,7 @@
     corruption_detected = true;
     auto error = RecalculateStoreStatusAndCommitTransaction(transaction);
     return error == Error::kOk
-               ? ResIdAndHashKeyListOrError(std::move(deleted_enties))
+               ? ResIdListOrError(std::move(res_ids_to_be_deleted))
                : base::unexpected(error);
   }
 
@@ -1228,7 +1192,7 @@
       -static_cast<int64_t>(res_ids_to_be_deleted.size()),
       /*total_size_delta=*/total_size_delta.ValueOrDie(), corruption_detected);
   return error == Error::kOk
-             ? ResIdAndHashKeyListOrError(std::move(deleted_enties))
+             ? ResIdListOrError(std::move(res_ids_to_be_deleted))
              : base::unexpected(error);
 }
 
@@ -1292,18 +1256,19 @@
       /*total_size_delta=*/-store_status_.total_size, corruption_detected);
 }
 
-ResIdAndHashKeyListOrErrorAndEvictionRequested
-Backend::DeleteLiveEntriesBetween(base::Time initial_time,
-                                  base::Time end_time,
-                                  base::flat_set<CacheEntryKey> excluded_keys,
-                                  base::TimeTicks start_time) {
+ResIdListOrErrorAndEvictionRequested Backend::DeleteLiveEntriesBetween(
+    base::Time initial_time,
+    base::Time end_time,
+    base::flat_set<ResId> excluded_res_ids,
+    base::TimeTicks start_time) {
   const base::TimeDelta posting_delay = base::TimeTicks::Now() - start_time;
   TRACE_EVENT_BEGIN1("disk_cache", "SqlBackend.DeleteLiveEntriesBetween",
                      "data", [&](perfetto::TracedValue trace_context) {
                        auto dict = std::move(trace_context).WriteDictionary();
                        dict.Add("initial_time", initial_time);
                        dict.Add("end_time", end_time);
-                       dict.Add("excluded_keys_size", excluded_keys.size());
+                       dict.Add("excluded_res_ids_size",
+                                excluded_res_ids.size());
                        PopulateTraceDetails(store_status_, dict);
                      });
   base::ElapsedTimer timer;
@@ -1311,7 +1276,7 @@
   // DeleteLiveEntriesBetween, database corruption is ignored.
   bool corruption_detected = false;
   auto result = DeleteLiveEntriesBetweenInternal(
-      initial_time, end_time, excluded_keys, corruption_detected);
+      initial_time, end_time, excluded_res_ids, corruption_detected);
   RecordTimeAndErrorResultHistogram(
       "DeleteLiveEntriesBetween", posting_delay, timer.Elapsed(),
       result.error_or(Error::kOk), corruption_detected);
@@ -1321,14 +1286,14 @@
                      PopulateTraceDetails(result, store_status_, dict);
                    });
   MaybeCrashIfCorrupted(corruption_detected);
-  return ResIdAndHashKeyListOrErrorAndEvictionRequested(std::move(result),
-                                                        ShouldStartEviction());
+  return ResIdListOrErrorAndEvictionRequested(std::move(result),
+                                              ShouldStartEviction());
 }
 
-ResIdAndHashKeyListOrError Backend::DeleteLiveEntriesBetweenInternal(
+ResIdListOrError Backend::DeleteLiveEntriesBetweenInternal(
     base::Time initial_time,
     base::Time end_time,
-    const base::flat_set<CacheEntryKey>& excluded_keys,
+    const base::flat_set<ResId>& excluded_res_ids,
     bool& corruption_detected) {
   if (simulate_db_failure_for_testing_) {
     return base::unexpected(Error::kFailedForTesting);
@@ -1339,8 +1304,7 @@
     return base::unexpected(Error::kFailedToStartTransaction);
   }
 
-  std::vector<ResId> res_ids_to_be_deleted;
-  ResIdAndHashKeyList deleted_enties;
+  ResIdList res_ids_to_be_deleted;
   base::CheckedNumeric<int64_t> total_size_delta = 0;
   {
     sql::Statement statement(db_.GetCachedStatement(
@@ -1349,12 +1313,10 @@
     statement.BindTime(0, initial_time);
     statement.BindTime(1, end_time);
     while (statement.Step()) {
-      const auto cache_key = CacheEntryKey(statement.ColumnString(2));
-      if (excluded_keys.contains(cache_key)) {
+      const auto res_id = ResId(statement.ColumnInt64(0));
+      if (excluded_res_ids.contains(res_id)) {
         continue;
       }
-      const auto res_id = ResId(statement.ColumnInt64(0));
-      deleted_enties.emplace_back(res_id, cache_key.hash());
       res_ids_to_be_deleted.emplace_back(res_id);
       total_size_delta -= statement.ColumnInt64(1);
     }
@@ -1379,17 +1341,17 @@
     corruption_detected = true;
     auto error = RecalculateStoreStatusAndCommitTransaction(transaction);
     return error == Error::kOk
-               ? ResIdAndHashKeyListOrError(std::move(deleted_enties))
+               ? ResIdListOrError(std::move(res_ids_to_be_deleted))
                : base::unexpected(error);
   }
 
   // Update the in-memory and on-disk store status (entry count and total size)
   // and commit the transaction.
   auto error = UpdateStoreStatusAndCommitTransaction(
-      transaction, -static_cast<int64_t>(deleted_enties.size()),
+      transaction, -static_cast<int64_t>(res_ids_to_be_deleted.size()),
       total_size_delta.ValueOrDie(), corruption_detected);
   return error == Error::kOk
-             ? ResIdAndHashKeyListOrError(std::move(deleted_enties))
+             ? ResIdListOrError(std::move(res_ids_to_be_deleted))
              : base::unexpected(error);
 }
 
@@ -2288,25 +2250,24 @@
   return std::nullopt;
 }
 
-ResIdAndHashKeyListOrErrorAndEvictionRequested Backend::RunEviction(
-    base::flat_set<CacheEntryKey> excluded_keys,
+ResIdListOrErrorAndEvictionRequested Backend::RunEviction(
+    base::flat_set<ResId> excluded_res_ids,
     base::TimeTicks start_time) {
   const base::TimeDelta posting_delay = base::TimeTicks::Now() - start_time;
   TRACE_EVENT0("disk_cache", "SqlBackend.RunEviction");
   base::ElapsedTimer timer;
   bool corruption_detected = false;
-  auto result =
-      RunEvictionInternal(std::move(excluded_keys), corruption_detected);
+  auto result = RunEvictionInternal(excluded_res_ids, corruption_detected);
   RecordTimeAndErrorResultHistogram(
       "RunEviction", posting_delay, timer.Elapsed(),
       result.error_or(Error::kOk), corruption_detected);
   MaybeCrashIfCorrupted(corruption_detected);
-  return ResIdAndHashKeyListOrErrorAndEvictionRequested(std::move(result),
-                                                        ShouldStartEviction());
+  return ResIdListOrErrorAndEvictionRequested(std::move(result),
+                                              ShouldStartEviction());
 }
 
-ResIdAndHashKeyListOrError Backend::RunEvictionInternal(
-    const base::flat_set<CacheEntryKey>& excluded_keys,
+ResIdListOrError Backend::RunEvictionInternal(
+    const base::flat_set<ResId>& excluded_res_ids,
     bool& corruption_detected) {
   int64_t size_to_be_removed = GetSizeOfAllEntries() - low_watermark_;
   sql::Transaction transaction(&db_);
@@ -2314,26 +2275,24 @@
     return base::unexpected(Error::kFailedToExecute);
   }
 
-  std::vector<ResId> res_ids_to_be_deleted;
+  ResIdList res_ids_to_be_deleted;
   int64_t entry_count_delta = 0;
   // Use checked numerics to safely update the total cache size.
   base::CheckedNumeric<int64_t> checked_total_size_delta = 0;
   base::CheckedNumeric<int64_t> checked_removed_total_size = 0;
-  ResIdAndHashKeyList deleted_enties;
+  ResIdList deleted_enties;
   {
     sql::Statement statement(db_.GetCachedStatement(
         SQL_FROM_HERE, GetQuery(Query::kRunEviction_SelectLiveResources)));
     while (size_to_be_removed > checked_removed_total_size.ValueOrDie() &&
            statement.Step()) {
       const ResId res_id = ResId(statement.ColumnInt64(0));
-      const CacheEntryKey cache_key = CacheEntryKey(statement.ColumnString(1));
-      if (excluded_keys.contains(cache_key)) {
+      if (excluded_res_ids.contains(res_id)) {
         continue;
       }
-      deleted_enties.emplace_back(res_id, cache_key.hash());
       res_ids_to_be_deleted.emplace_back(res_id);
       --entry_count_delta;
-      const int64_t bytes_usage = statement.ColumnInt64(2);
+      const int64_t bytes_usage = statement.ColumnInt64(1);
       checked_total_size_delta -= bytes_usage;
       checked_removed_total_size += bytes_usage;
       checked_removed_total_size += kSqlBackendStaticResourceSize;
@@ -2361,7 +2320,7 @@
       transaction, entry_count_delta, checked_total_size_delta.ValueOrDie(),
       corruption_detected);
   return error == Error::kOk
-             ? ResIdAndHashKeyListOrError(std::move(deleted_enties))
+             ? ResIdListOrError(std::move(res_ids_to_be_deleted))
              : base::unexpected(error);
 }
 
@@ -2534,6 +2493,8 @@
                 if (result.has_value()) {
                   weak_ptr->SetMaxSize(result->max_bytes);
                   weak_ptr->index_ = std::move(result->index);
+                  weak_ptr->to_be_deleted_res_ids_ =
+                      std::move(result->doomed_entry_res_ids);
                 }
                 std::move(callback).Run(result.has_value() ? Error::kOk
                                                            : result.error());
@@ -2597,12 +2558,6 @@
         .WithArgs(key, res_id, base::TimeTicks::Now())
         .Then(WrapCallbackWithEvictionRequested(std::move(callback)));
   }
-  void DeleteDoomedEntries(base::flat_set<ResId> excluded_res_ids,
-                           ErrorCallback callback) override {
-    backend_.AsyncCall(&Backend::DeleteDoomedEntries)
-        .WithArgs(std::move(excluded_res_ids), base::TimeTicks::Now())
-        .Then(WrapCallback(std::move(callback)));
-  }
   void DeleteLiveEntry(const CacheEntryKey& key,
                        ErrorCallback callback) override {
     backend_.AsyncCall(&Backend::DeleteLiveEntry)
@@ -2629,10 +2584,10 @@
   }
   void DeleteLiveEntriesBetween(base::Time initial_time,
                                 base::Time end_time,
-                                base::flat_set<CacheEntryKey> excluded_keys,
+                                base::flat_set<ResId> excluded_res_ids,
                                 ErrorCallback callback) override {
     backend_.AsyncCall(&Backend::DeleteLiveEntriesBetween)
-        .WithArgs(initial_time, end_time, std::move(excluded_keys),
+        .WithArgs(initial_time, end_time, std::move(excluded_res_ids),
                   base::TimeTicks::Now())
         .Then(WrapErrorCallbackToRemoveFromIndex(
             std::move(callback),
@@ -2714,23 +2669,22 @@
   bool ShouldStartEviction() override {
     return !eviction_in_progress_ && eviction_requested_;
   }
-  void StartEviction(base::flat_set<CacheEntryKey> excluded_keys,
+  void StartEviction(base::flat_set<ResId> excluded_res_ids,
                      ErrorCallback callback) override {
     CHECK(!eviction_in_progress_);
     eviction_in_progress_ = true;
     backend_.AsyncCall(&Backend::RunEviction)
-        .WithArgs(std::move(excluded_keys), base::TimeTicks::Now())
+        .WithArgs(std::move(excluded_res_ids), base::TimeTicks::Now())
         .Then(base::BindOnce(
             [](base::WeakPtr<SqlPersistentStoreImpl> weak_ptr,
                ErrorCallback callback,
-               ResIdAndHashKeyListOrErrorAndEvictionRequested result) {
+               ResIdListOrErrorAndEvictionRequested result) {
               if (weak_ptr) {
                 weak_ptr->eviction_in_progress_ = false;
                 if (result.result.has_value()) {
                   CHECK(weak_ptr->index_.has_value());
-                  for (const auto& res_id_and_key : *result.result) {
-                    if (!weak_ptr->index_->Remove(res_id_and_key.key_hash,
-                                                  res_id_and_key.res_id)) {
+                  for (ResId res_id : *result.result) {
+                    if (!weak_ptr->index_->Remove(res_id)) {
                       weak_ptr->RecordIndexMismatch(
                           IndexMismatchLocation::kStartEviction);
                     }
@@ -2751,6 +2705,15 @@
   void GetSizeOfAllEntries(Int64Callback callback) const override {
     backend_.AsyncCall(&Backend::GetSizeOfAllEntries).Then(std::move(callback));
   }
+  bool MaybeRunCleanupDoomedEntries(ErrorCallback callback) override {
+    if (to_be_deleted_res_ids_.empty()) {
+      return false;
+    }
+    backend_.AsyncCall(&Backend::DeleteDoomedEntries)
+        .WithArgs(std::move(to_be_deleted_res_ids_), base::TimeTicks::Now())
+        .Then(WrapCallback(std::move(callback)));
+    return true;
+  }
   void MaybeRunCheckpoint(base::OnceCallback<void(bool)> callback) override {
     backend_.AsyncCall(&Backend::MaybeRunCheckpoint).Then(std::move(callback));
   }
@@ -2842,19 +2805,18 @@
         weak_factory_.GetWeakPtr(), std::move(callback), key.hash(), location);
   }
 
-  base::OnceCallback<void(ResIdAndHashKeyListOrErrorAndEvictionRequested)>
+  base::OnceCallback<void(ResIdListOrErrorAndEvictionRequested)>
   WrapErrorCallbackToRemoveFromIndex(ErrorCallback callback,
                                      IndexMismatchLocation location) {
     return base::BindOnce(
         [](base::WeakPtr<SqlPersistentStoreImpl> weak_ptr,
            ErrorCallback callback, IndexMismatchLocation location,
-           ResIdAndHashKeyListOrErrorAndEvictionRequested result) {
+           ResIdListOrErrorAndEvictionRequested result) {
           if (weak_ptr) {
             if (result.result.has_value()) {
               CHECK(weak_ptr->index_.has_value());
-              for (const auto& hash_and_res_id : result.result.value()) {
-                if (!weak_ptr->index_->Remove(hash_and_res_id.key_hash,
-                                              hash_and_res_id.res_id)) {
+              for (ResId res_id : result.result.value()) {
+                if (!weak_ptr->index_->Remove(res_id)) {
                   weak_ptr->RecordIndexMismatch(location);
                 }
               }
@@ -2882,7 +2844,12 @@
   bool eviction_requested_ = false;
   bool strict_corruption_check_enabled_ = false;
 
-  std::optional<HashResIdSet> index_;
+  std::optional<SqlPersistentStoreInMemoryIndex> index_;
+
+  // A list of resource IDs for entries that were doomed in a previous session
+  // and are scheduled for deletion.
+  ResIdList to_be_deleted_res_ids_;
+
   base::WeakPtrFactory<SqlPersistentStoreImpl> weak_factory_{this};
 };
 
diff --git a/net/disk_cache/sql/sql_persistent_store.h b/net/disk_cache/sql/sql_persistent_store.h
index f1a7632..0a264ece 100644
--- a/net/disk_cache/sql/sql_persistent_store.h
+++ b/net/disk_cache/sql/sql_persistent_store.h
@@ -176,13 +176,6 @@
                                  ResId res_id,
                                  ErrorCallback callback) = 0;
 
-  // Physically deletes all entries that have been marked as doomed, except for
-  // those whose IDs are in `excluded_res_ids`. This is typically used for
-  // background cleanup of doomed entries that are no longer in use. `callback`
-  // is invoked upon completion.
-  virtual void DeleteDoomedEntries(base::flat_set<ResId> excluded_res_ids,
-                                   ErrorCallback callback) = 0;
-
   // Deletes a "live" entry, i.e., an entry whose `doomed` flag is not set.
   // This is for use for entries which are not open; open entries should have
   // `DoomEntry()` called, and then `DeleteDoomedEntry()` once they're no longer
@@ -195,12 +188,11 @@
 
   // Deletes all "live" (not doomed) entries whose `last_used` time falls
   // within the range [`initial_time`, `end_time`), excluding any entries whose
-  // keys are present in `excluded_keys`. `callback` is invoked on completion.
-  virtual void DeleteLiveEntriesBetween(
-      base::Time initial_time,
-      base::Time end_time,
-      base::flat_set<CacheEntryKey> excluded_keys,
-      ErrorCallback callback) = 0;
+  // IDs are present in `excluded_res_ids`. `callback` is invoked on completion.
+  virtual void DeleteLiveEntriesBetween(base::Time initial_time,
+                                        base::Time end_time,
+                                        base::flat_set<ResId> excluded_res_ids,
+                                        ErrorCallback callback) = 0;
 
   // Updates the `last_used` timestamp for the entry with the specified `key`.
   // `callback` is invoked with `kOk` on success, or `kNotFound` if the entry
@@ -308,9 +300,9 @@
 
   // Starts the eviction process to reduce the cache size. This method removes
   // the least recently used entries until the total cache size is below the
-  // low watermark. Entries with keys in `excluded_keys` (typically active
+  // low watermark. Entries with ResId in `excluded_res_ids` (typically active
   // entries) will not be evicted. `callback` is invoked upon completion.
-  virtual void StartEviction(base::flat_set<CacheEntryKey> excluded_keys,
+  virtual void StartEviction(base::flat_set<ResId> excluded_res_ids,
                              ErrorCallback callback) = 0;
 
   // The maximum size of an individual cache entry's data stream.
@@ -325,6 +317,12 @@
   // Asynchronously retrieves the total size of all entries.
   virtual void GetSizeOfAllEntries(Int64Callback callback) const = 0;
 
+  // If there are entries that were doomed in a previous session, this method
+  // triggers a task to delete them from the database. The cleanup is performed
+  // in the background. Returns true if a cleanup task was scheduled, and false
+  // otherwise. `callback` is invoked upon completion of the cleanup task.
+  virtual bool MaybeRunCleanupDoomedEntries(ErrorCallback callback) = 0;
+
   // If the browser is idle and the number of pages recorded in the WAL exceeds
   // kSqlDiskCacheIdleCheckpointThreshold, a checkpoint is executed.
   virtual void MaybeRunCheckpoint(base::OnceCallback<void(bool)> callback) = 0;
diff --git a/net/disk_cache/sql/sql_persistent_store_in_memory_index.cc b/net/disk_cache/sql/sql_persistent_store_in_memory_index.cc
new file mode 100644
index 0000000..7ea5acff
--- /dev/null
+++ b/net/disk_cache/sql/sql_persistent_store_in_memory_index.cc
@@ -0,0 +1,68 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/disk_cache/sql/sql_persistent_store_in_memory_index.h"
+
+namespace disk_cache {
+
+SqlPersistentStoreInMemoryIndex::SqlPersistentStoreInMemoryIndex() = default;
+SqlPersistentStoreInMemoryIndex::~SqlPersistentStoreInMemoryIndex() = default;
+
+SqlPersistentStoreInMemoryIndex::SqlPersistentStoreInMemoryIndex(
+    SqlPersistentStoreInMemoryIndex&& other) noexcept = default;
+SqlPersistentStoreInMemoryIndex& SqlPersistentStoreInMemoryIndex::operator=(
+    SqlPersistentStoreInMemoryIndex&& other) noexcept = default;
+
+bool SqlPersistentStoreInMemoryIndex::Insert(CacheEntryKey::Hash hash,
+                                             SqlPersistentStore::ResId res_id) {
+  if (res_id_to_hash_map_.contains(res_id)) {
+    return false;
+  }
+  if (hash_res_id_set_.Insert(hash, res_id)) {
+    res_id_to_hash_map_[res_id] = hash;
+    return true;
+  }
+  return false;
+}
+
+bool SqlPersistentStoreInMemoryIndex::Contains(CacheEntryKey::Hash hash) const {
+  return hash_res_id_set_.Contains(hash);
+}
+
+bool SqlPersistentStoreInMemoryIndex::Remove(SqlPersistentStore::ResId res_id) {
+  auto it = res_id_to_hash_map_.find(res_id);
+  if (it == res_id_to_hash_map_.end()) {
+    return false;
+  }
+  return RemoveInternal(it);
+}
+
+bool SqlPersistentStoreInMemoryIndex::Remove(CacheEntryKey::Hash hash,
+                                             SqlPersistentStore::ResId res_id) {
+  auto it = res_id_to_hash_map_.find(res_id);
+  if (it == res_id_to_hash_map_.end()) {
+    return false;
+  }
+  if (it->second != hash) {
+    return false;
+  }
+  return RemoveInternal(it);
+}
+
+void SqlPersistentStoreInMemoryIndex::Clear() {
+  hash_res_id_set_.Clear();
+  res_id_to_hash_map_.clear();
+}
+
+bool SqlPersistentStoreInMemoryIndex::RemoveInternal(
+    ResIdToHashMap::iterator it) {
+  DCHECK(it != res_id_to_hash_map_.end());
+  if (!hash_res_id_set_.Remove(it->second, it->first)) {
+    return false;
+  }
+  res_id_to_hash_map_.erase(it);
+  return true;
+}
+
+}  // namespace disk_cache
diff --git a/net/disk_cache/sql/sql_persistent_store_in_memory_index.h b/net/disk_cache/sql/sql_persistent_store_in_memory_index.h
new file mode 100644
index 0000000..bd006b84
--- /dev/null
+++ b/net/disk_cache/sql/sql_persistent_store_in_memory_index.h
@@ -0,0 +1,50 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DISK_CACHE_SQL_SQL_PERSISTENT_STORE_IN_MEMORY_INDEX_H_
+#define NET_DISK_CACHE_SQL_SQL_PERSISTENT_STORE_IN_MEMORY_INDEX_H_
+
+#include "net/base/net_export.h"
+#include "net/disk_cache/sql/indexed_pair_set.h"
+#include "net/disk_cache/sql/sql_persistent_store.h"
+
+namespace disk_cache {
+
+// A class that holds an in-memory index of the cache entries. It provides fast
+// lookups of cache entries by their hash and resource ID.
+class NET_EXPORT_PRIVATE SqlPersistentStoreInMemoryIndex {
+ public:
+  SqlPersistentStoreInMemoryIndex();
+  ~SqlPersistentStoreInMemoryIndex();
+
+  SqlPersistentStoreInMemoryIndex(const SqlPersistentStoreInMemoryIndex&) =
+      delete;
+  SqlPersistentStoreInMemoryIndex& operator=(
+      const SqlPersistentStoreInMemoryIndex&) = delete;
+  SqlPersistentStoreInMemoryIndex(
+      SqlPersistentStoreInMemoryIndex&& other) noexcept;
+  SqlPersistentStoreInMemoryIndex& operator=(
+      SqlPersistentStoreInMemoryIndex&& other) noexcept;
+
+  bool Insert(CacheEntryKey::Hash hash, SqlPersistentStore::ResId res_id);
+  bool Contains(CacheEntryKey::Hash hash) const;
+  bool Remove(SqlPersistentStore::ResId res_id);
+  bool Remove(CacheEntryKey::Hash hash, SqlPersistentStore::ResId res_id);
+  void Clear();
+
+ private:
+  using HashResIdSet =
+      IndexedPairSet<CacheEntryKey::Hash, SqlPersistentStore::ResId>;
+  using ResIdToHashMap =
+      absl::flat_hash_map<SqlPersistentStore::ResId, CacheEntryKey::Hash>;
+
+  bool RemoveInternal(ResIdToHashMap::iterator it);
+
+  HashResIdSet hash_res_id_set_;
+  ResIdToHashMap res_id_to_hash_map_;
+};
+
+}  // namespace disk_cache
+
+#endif  // NET_DISK_CACHE_SQL_SQL_PERSISTENT_STORE_IN_MEMORY_INDEX_H_
diff --git a/net/disk_cache/sql/sql_persistent_store_in_memory_index_unittest.cc b/net/disk_cache/sql/sql_persistent_store_in_memory_index_unittest.cc
new file mode 100644
index 0000000..7c424ba6
--- /dev/null
+++ b/net/disk_cache/sql/sql_persistent_store_in_memory_index_unittest.cc
@@ -0,0 +1,89 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/disk_cache/sql/sql_persistent_store_in_memory_index.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace disk_cache {
+
+namespace {
+
+const CacheEntryKey::Hash kHash1(1);
+const SqlPersistentStore::ResId kResId1(1);
+const CacheEntryKey::Hash kHash2(2);
+const SqlPersistentStore::ResId kResId2(2);
+
+}  // namespace
+
+TEST(SqlPersistentStoreInMemoryIndexTest, Insert) {
+  SqlPersistentStoreInMemoryIndex index;
+  EXPECT_TRUE(index.Insert(kHash1, kResId1));
+  EXPECT_TRUE(index.Contains(kHash1));
+}
+
+TEST(SqlPersistentStoreInMemoryIndexTest, InsertDuplicateResId) {
+  SqlPersistentStoreInMemoryIndex index;
+  EXPECT_TRUE(index.Insert(kHash1, kResId1));
+  EXPECT_FALSE(index.Insert(kHash2, kResId1));
+  EXPECT_TRUE(index.Contains(kHash1));
+}
+
+TEST(SqlPersistentStoreInMemoryIndexTest, InsertSameHash) {
+  SqlPersistentStoreInMemoryIndex index;
+  EXPECT_TRUE(index.Insert(kHash1, kResId1));
+  EXPECT_TRUE(index.Insert(kHash1, kResId2));
+  EXPECT_TRUE(index.Contains(kHash1));
+}
+
+TEST(SqlPersistentStoreInMemoryIndexTest, RemoveWithHashAndResId) {
+  SqlPersistentStoreInMemoryIndex index;
+  index.Insert(kHash1, kResId1);
+  EXPECT_TRUE(index.Remove(kHash1, kResId1));
+  EXPECT_FALSE(index.Contains(kHash1));
+}
+
+TEST(SqlPersistentStoreInMemoryIndexTest, RemoveWithResId) {
+  SqlPersistentStoreInMemoryIndex index;
+  index.Insert(kHash1, kResId1);
+  EXPECT_TRUE(index.Remove(kResId1));
+  EXPECT_FALSE(index.Contains(kHash1));
+}
+
+TEST(SqlPersistentStoreInMemoryIndexTest, RemoveNonExistent) {
+  SqlPersistentStoreInMemoryIndex index;
+  index.Insert(kHash1, kResId1);
+  EXPECT_FALSE(index.Remove(kResId2));
+  EXPECT_FALSE(index.Remove(kHash2, kResId2));
+  EXPECT_FALSE(index.Remove(kHash2, kResId1));
+  EXPECT_FALSE(index.Remove(kHash1, kResId2));
+  EXPECT_TRUE(index.Contains(kHash1));
+}
+
+TEST(SqlPersistentStoreInMemoryIndexTest, Clear) {
+  SqlPersistentStoreInMemoryIndex index;
+  index.Insert(kHash1, kResId1);
+  index.Insert(kHash2, kResId2);
+  index.Clear();
+  EXPECT_FALSE(index.Contains(kHash1));
+  EXPECT_FALSE(index.Contains(kHash2));
+}
+
+TEST(SqlPersistentStoreInMemoryIndexTest, MultipleEntries) {
+  SqlPersistentStoreInMemoryIndex index;
+  index.Insert(kHash1, kResId1);
+  index.Insert(kHash2, kResId2);
+
+  EXPECT_TRUE(index.Contains(kHash1));
+  EXPECT_TRUE(index.Contains(kHash2));
+
+  EXPECT_TRUE(index.Remove(kResId1));
+  EXPECT_FALSE(index.Contains(kHash1));
+  EXPECT_TRUE(index.Contains(kHash2));
+
+  EXPECT_TRUE(index.Remove(kHash2, kResId2));
+  EXPECT_FALSE(index.Contains(kHash2));
+}
+
+}  // namespace disk_cache
diff --git a/net/disk_cache/sql/sql_persistent_store_queries.h b/net/disk_cache/sql/sql_persistent_store_queries.h
index e996611..2716d70 100644
--- a/net/disk_cache/sql/sql_persistent_store_queries.h
+++ b/net/disk_cache/sql/sql_persistent_store_queries.h
@@ -62,17 +62,13 @@
     "CREATE INDEX index_resources_cache_key_hash_doomed ON "
     "resources(cache_key_hash, doomed)";
 
-// An index on `last_used` for live entries (`doomed=0`). This is crucial for
-// eviction logic, which targets the least recently used entries.
+// An index on `last_used` and `bytes_usage` for live entries (`doomed=0`). This
+// is crucial for eviction logic, which targets the least recently used entries.
+// To avoid looking at the actual resources table during eviction, this creates
+// a covering index.
 inline constexpr const char kIndex_LiveResourcesLastUsed[] =
-    "CREATE INDEX index_live_resources_last_used ON "
-    "resources(last_used) WHERE doomed=0";
-
-// An index on `res_id` for doomed entries (`doomed=1`). This is used to
-// efficiently find and clean up doomed entries.
-inline constexpr const char kIndex_DoomedResourcesResId[] =
-    "CREATE INDEX index_doomed_resources_res_id ON "
-    "resources(res_id) WHERE doomed=1";
+    "CREATE INDEX index_live_resources_last_used_bytes_usage ON "
+    "resources(last_used, bytes_usage) WHERE doomed=0";
 
 // A unique index on `(res_id, start)` in the `blobs` table. This is critical
 // for quickly finding the correct data blobs for a given entry when reading or
@@ -131,14 +127,6 @@
         "doomed=1";
 // clang-format on
 
-inline constexpr const char kDeleteDoomedEntries_SelectDoomedResources[] =
-    // clang-format off
-    "SELECT "
-        "res_id "       // 0
-    "FROM resources "
-    "WHERE doomed=1";
-// clang-format on
-
 inline constexpr const char kDeleteLiveEntry_DeleteFromResources[] =
     // clang-format off
     "DELETE FROM resources "
@@ -161,8 +149,7 @@
     // clang-format off
     "SELECT "
         "res_id,"       // 0
-        "bytes_usage,"  // 1
-        "cache_key "    // 2
+        "bytes_usage "  // 1
     "FROM resources "
     "WHERE "
         "last_used>=? AND "  // 0
@@ -344,8 +331,7 @@
     // clang-format off
     "SELECT "
         "res_id,"       // 0
-        "cache_key,"    // 1
-        "bytes_usage "  // 2
+        "bytes_usage "  // 1
     "FROM resources "
     "WHERE "
         "doomed=0 "
@@ -387,13 +373,11 @@
 
   kIndex_ResourcesCacheKeyHashDoomed,
   kIndex_LiveResourcesLastUsed,
-  kIndex_DoomedResourcesResId,
   kIndex_BlobsResIdStart,
   kOpenEntry_SelectLiveResources,
   kCreateEntry_InsertIntoResources,
   kDoomEntry_MarkDoomedResources,
   kDeleteDoomedEntry_DeleteFromResources,
-  kDeleteDoomedEntries_SelectDoomedResources,
   kDeleteLiveEntry_DeleteFromResources,
   kDeleteAllEntries_DeleteFromResources,
   kDeleteAllEntries_DeleteFromBlobs,
@@ -433,8 +417,6 @@
       return internal::kIndex_ResourcesCacheKeyHashDoomed;
     case Query::kIndex_LiveResourcesLastUsed:
       return internal::kIndex_LiveResourcesLastUsed;
-    case Query::kIndex_DoomedResourcesResId:
-      return internal::kIndex_DoomedResourcesResId;
     case Query::kIndex_BlobsResIdStart:
       return internal::kIndex_BlobsResIdStart;
     case Query::kOpenEntry_SelectLiveResources:
@@ -445,8 +427,6 @@
       return internal::kDoomEntry_MarkDoomedResources;
     case Query::kDeleteDoomedEntry_DeleteFromResources:
       return internal::kDeleteDoomedEntry_DeleteFromResources;
-    case Query::kDeleteDoomedEntries_SelectDoomedResources:
-      return internal::kDeleteDoomedEntries_SelectDoomedResources;
     case Query::kDeleteLiveEntry_DeleteFromResources:
       return internal::kDeleteLiveEntry_DeleteFromResources;
     case Query::kDeleteAllEntries_DeleteFromResources:
diff --git a/net/disk_cache/sql/sql_persistent_store_queries_unittest.cc b/net/disk_cache/sql/sql_persistent_store_queries_unittest.cc
index 7aaf9222..eeda9ef 100644
--- a/net/disk_cache/sql/sql_persistent_store_queries_unittest.cc
+++ b/net/disk_cache/sql/sql_persistent_store_queries_unittest.cc
@@ -39,7 +39,6 @@
     Query::kInitSchema_CreateTableBlobs,
     Query::kIndex_ResourcesCacheKeyHashDoomed,
     Query::kIndex_LiveResourcesLastUsed,
-    Query::kIndex_DoomedResourcesResId,
     Query::kIndex_BlobsResIdStart,
 });
 
@@ -137,9 +136,6 @@
            {Query::kDeleteDoomedEntry_DeleteFromResources,
             "`--SEARCH resources USING "
             "INTEGER PRIMARY KEY (rowid=?)"},
-           {Query::kDeleteDoomedEntries_SelectDoomedResources,
-            "`--SCAN resources USING "
-            "COVERING INDEX index_doomed_resources_res_id"},
            {Query::kDeleteLiveEntry_DeleteFromResources,
             "`--SEARCH resources USING "
             "INDEX index_resources_cache_key_hash_doomed "
@@ -148,7 +144,7 @@
            {Query::kDeleteAllEntries_DeleteFromBlobs, ""},
            {Query::kDeleteLiveEntriesBetween_SelectLiveResources,
             "`--SEARCH resources USING "
-            "INDEX index_live_resources_last_used "
+            "COVERING INDEX index_live_resources_last_used_bytes_usage "
             "(last_used>? AND last_used<?)"},
            {Query::kDeleteResourcesByResIds_DeleteFromResources,
             "`--SEARCH resources USING "
@@ -196,22 +192,23 @@
             "(res_id=? AND start<?)"},
            {Query::kCalculateSizeOfEntriesBetween_SelectLiveResources,
             "`--SEARCH resources USING "
-            "INDEX index_live_resources_last_used "
+            "COVERING INDEX index_live_resources_last_used_bytes_usage "
             "(last_used>? AND last_used<?)"},
            {Query::kOpenLatestEntryBeforeResId_SelectLiveResources,
             "`--SEARCH resources USING "
             "INTEGER PRIMARY KEY (rowid<?)"},
            {Query::kRunEviction_SelectLiveResources,
-            "`--SCAN resources USING INDEX index_live_resources_last_used"},
+            "`--SCAN resources USING "
+            "COVERING INDEX index_live_resources_last_used_bytes_usage"},
            {Query::kRunEviction_DeleteFromResources,
             "`--SEARCH resources USING "
             "INTEGER PRIMARY KEY (rowid=?)"},
            {Query::kCalculateResourceEntryCount_SelectCountFromLiveResources,
             "`--SCAN resources USING "
-            "COVERING INDEX index_live_resources_last_used"},
+            "COVERING INDEX index_live_resources_last_used_bytes_usage"},
            {Query::kCalculateTotalSize_SelectTotalSizeFromLiveResources,
             "`--SCAN resources USING "
-            "INDEX index_live_resources_last_used"},
+            "COVERING INDEX index_live_resources_last_used_bytes_usage"},
            {Query::kGetCacheKeyHashes_SelectCacheKeyHashFromLiveResources,
             "`--SCAN resources USING COVERING INDEX "
             "index_resources_cache_key_hash_doomed"}});
diff --git a/net/disk_cache/sql/sql_persistent_store_unittest.cc b/net/disk_cache/sql/sql_persistent_store_unittest.cc
index 37d412c6..1b1f42f 100644
--- a/net/disk_cache/sql/sql_persistent_store_unittest.cc
+++ b/net/disk_cache/sql/sql_persistent_store_unittest.cc
@@ -233,14 +233,6 @@
     return future.Get();
   }
 
-  // Synchronous wrapper for DeleteDoomedEntries.
-  SqlPersistentStore::Error DeleteDoomedEntries(
-      base::flat_set<SqlPersistentStore::ResId> excluded_ids) {
-    base::test::TestFuture<SqlPersistentStore::Error> future;
-    store_->DeleteDoomedEntries(std::move(excluded_ids), future.GetCallback());
-    return future.Get();
-  }
-
   // Synchronous wrapper for DeleteLiveEntry.
   SqlPersistentStore::Error DeleteLiveEntry(const CacheEntryKey& key) {
     base::test::TestFuture<SqlPersistentStore::Error> future;
@@ -268,10 +260,10 @@
   SqlPersistentStore::Error DeleteLiveEntriesBetween(
       base::Time initial_time,
       base::Time end_time,
-      base::flat_set<CacheEntryKey> excluded_keys = {}) {
+      base::flat_set<SqlPersistentStore::ResId> excluded_ids = {}) {
     base::test::TestFuture<SqlPersistentStore::Error> future;
     store_->DeleteLiveEntriesBetween(
-        initial_time, end_time, std::move(excluded_keys), future.GetCallback());
+        initial_time, end_time, std::move(excluded_ids), future.GetCallback());
     return future.Get();
   }
 
@@ -1300,7 +1292,7 @@
   EXPECT_EQ(GetSizeOfAllEntries(), 0);
 }
 
-TEST_F(SqlPersistentStoreTest, DeleteDoomedEntries) {
+TEST_F(SqlPersistentStoreTest, MaybeRunCleanupDoomedEntries) {
   CreateAndInitStore();
   const CacheEntryKey kKeyToDoom1("key-to-doom1");
   const CacheEntryKey kKeyToDoom2("key-to-doom2");
@@ -1317,19 +1309,13 @@
   ASSERT_TRUE(create_result2.has_value());
   const auto res_id_to_doom2 = create_result2->res_id;
 
-  // Create an entry that will be doomed but also excluded from deletion.
-  auto create_result3 = CreateEntry(kKeyToDoomActive);
-  ASSERT_TRUE(create_result3.has_value());
-  const auto res_id_to_doom_active = create_result3->res_id;
-
   // Create an entry that will be kept.
-  auto create_result4 = CreateEntry(kKeyToKeep);
-  // There should be 4 created entries.
-  ASSERT_TRUE(create_result4.has_value());
-  const auto res_id_to_keep = create_result4->res_id;
+  auto create_result3 = CreateEntry(kKeyToKeep);
+  ASSERT_TRUE(create_result3.has_value());
+  const auto res_id_to_keep = create_result3->res_id;
 
-  // There should be 4 created entries.
-  ASSERT_EQ(GetEntryCount(), 4);
+  // There should be 3 created entries.
+  ASSERT_EQ(GetEntryCount(), 3);
 
   // Write data to the entries that will be doomed.
   const std::string kData1 = "doomed_data1";
@@ -1338,35 +1324,31 @@
   const std::string kData2 = "doomed_data2";
   WriteDataAndAssertSuccess(kKeyToDoom2, res_id_to_doom2, /*old_body_end=*/0,
                             /*offset=*/0, kData2, /*truncate=*/false);
-  const std::string kData3 = "doomed_active_data";
-  WriteDataAndAssertSuccess(kKeyToDoomActive, res_id_to_doom_active,
-                            /*old_body_end=*/0,
-                            /*offset=*/0, kData3, /*truncate=*/false);
-  const std::string kData4 = "keep-data";
+  const std::string kData3 = "keep-data";
   WriteDataAndAssertSuccess(kKeyToKeep, res_id_to_keep, /*old_body_end=*/0,
-                            /*offset=*/0, kData4, /*truncate=*/false);
+                            /*offset=*/0, kData3, /*truncate=*/false);
   // Doom all the entries that will be doomed.
   ASSERT_EQ(DoomEntry(kKeyToDoom1, res_id_to_doom1),
             SqlPersistentStore::Error::kOk);
   ASSERT_EQ(DoomEntry(kKeyToDoom2, res_id_to_doom2),
             SqlPersistentStore::Error::kOk);
-  ASSERT_EQ(DoomEntry(kKeyToDoomActive, res_id_to_doom_active),
-            SqlPersistentStore::Error::kOk);
-  // The entry count after dooming 3 entries should be 1.
+  // The entry count after dooming 2 entries should be 1.
   ASSERT_EQ(GetEntryCount(), 1);
 
   // All resource blobs should be still available.
-  EXPECT_EQ(CountResourcesTable(), 4);
+  EXPECT_EQ(CountResourcesTable(), 3);
   CheckBlobData(res_id_to_doom1, {{0, kData1}});
   CheckBlobData(res_id_to_doom2, {{0, kData2}});
-  CheckBlobData(res_id_to_doom_active, {{0, kData3}});
-  CheckBlobData(res_id_to_keep, {{0, kData4}});
+  CheckBlobData(res_id_to_keep, {{0, kData3}});
+
+  // Reload the store and the doomed entries will be marked for deletion.
+  ClearStore();
+  CreateAndInitStore();
 
   base::HistogramTester histogram_tester;
-
-  // Delete all doomed entries except for kKeyToDoomActive.
-  ASSERT_EQ(DeleteDoomedEntries({res_id_to_doom_active}),
-            SqlPersistentStore::Error::kOk);
+  base::test::TestFuture<SqlPersistentStore::Error> future;
+  EXPECT_TRUE(store_->MaybeRunCleanupDoomedEntries(future.GetCallback()));
+  EXPECT_EQ(future.Get(), SqlPersistentStore::Error::kOk);
 
   // Verify that `DeleteDoomedEntriesCount` UMA was recorded in the histogram.
   histogram_tester.ExpectUniqueSample(
@@ -1374,11 +1356,10 @@
 
   // Verify the entries for kKeyToDoom1 and kKeyToDoom2 are physically gone from
   // the database.
-  EXPECT_EQ(CountResourcesTable(), 2);
+  EXPECT_EQ(CountResourcesTable(), 1);
   CheckBlobData(res_id_to_doom1, {});
   CheckBlobData(res_id_to_doom2, {});
-  CheckBlobData(res_id_to_doom_active, {{0, kData3}});
-  CheckBlobData(res_id_to_keep, {{0, kData4}});
+  CheckBlobData(res_id_to_keep, {{0, kData3}});
 
   // Verify the live entry is still present.
   auto open_result1 = OpenEntry(kKeyToKeep);
@@ -1387,39 +1368,29 @@
   EXPECT_EQ(open_result1.value()->res_id, res_id_to_keep);
 }
 
-TEST_F(SqlPersistentStoreTest, DeleteDoomedEntriesNoDeletion) {
+TEST_F(SqlPersistentStoreTest, MaybeRunCleanupDoomedEntriesNoDeletion) {
   CreateAndInitStore();
 
-  // Scenario 1: No doomed entries exist.
+  // Scenario 1: No entries exist.
   base::HistogramTester histogram_tester;
-  ASSERT_EQ(DeleteDoomedEntries({}), SqlPersistentStore::Error::kOk);
-  // Verify that the count histogram recorded a value of 0.
-  histogram_tester.ExpectUniqueSample(
-      "Net.SqlDiskCache.DeleteDoomedEntriesCount", 0, 1);
+
+  EXPECT_FALSE(store_->MaybeRunCleanupDoomedEntries(
+      base::BindOnce([](SqlPersistentStore::Error) { NOTREACHED(); })));
+
   EXPECT_EQ(CountResourcesTable(), 0);
 
-  // Scenario 2: All doomed entries are excluded.
-  const CacheEntryKey kKeyToDoom1("key-to-doom1");
-  const CacheEntryKey kKeyToDoom2("key-to-doom2");
-  auto create_result1 = CreateEntry(kKeyToDoom1);
+  // Scenario 2: All entries are not doomed.
+  const CacheEntryKey kKey1("key1");
+  const CacheEntryKey kKey2("key2");
+  auto create_result1 = CreateEntry(kKey1);
   ASSERT_TRUE(create_result1.has_value());
-  const auto res_id_to_doom1 = create_result1->res_id;
-  auto create_result2 = CreateEntry(kKeyToDoom2);
+  auto create_result2 = CreateEntry(kKey2);
   ASSERT_TRUE(create_result2.has_value());
-  const auto res_id_to_doom2 = create_result2->res_id;
-  ASSERT_EQ(DoomEntry(kKeyToDoom1, res_id_to_doom1),
-            SqlPersistentStore::Error::kOk);
-  ASSERT_EQ(DoomEntry(kKeyToDoom2, res_id_to_doom2),
-            SqlPersistentStore::Error::kOk);
   ASSERT_EQ(CountResourcesTable(), 2);
 
-  base::HistogramTester histogram_tester2;
-  // Exclude all doomed entries from deletion.
-  ASSERT_EQ(DeleteDoomedEntries({res_id_to_doom1, res_id_to_doom2}),
-            SqlPersistentStore::Error::kOk);
-  // Verify that the count histogram recorded a value of 0.
-  histogram_tester2.ExpectUniqueSample(
-      "Net.SqlDiskCache.DeleteDoomedEntriesCount", 0, 1);
+  EXPECT_FALSE(store_->MaybeRunCleanupDoomedEntries(
+      base::BindOnce([](SqlPersistentStore::Error) { NOTREACHED(); })));
+
   // Verify that no entries were deleted.
   EXPECT_EQ(CountResourcesTable(), 2);
 }
@@ -1587,7 +1558,9 @@
   const base::Time kTime1 = base::Time::Now();
 
   task_environment_.AdvanceClock(base::Minutes(1));
-  ASSERT_TRUE(CreateEntry(kKey2).has_value());
+  auto create_result = CreateEntry(kKey2);
+  ASSERT_TRUE(create_result.has_value());
+  SqlPersistentStore::ResId res_id2 = create_result->res_id;
 
   task_environment_.AdvanceClock(base::Minutes(1));
   ASSERT_TRUE(CreateEntry(kKey3).has_value());
@@ -1631,8 +1604,8 @@
   // kKey2 should be excluded.
   // Expected to delete: kKey1.
   // Expected to keep: kKey2, kKey3, kKey4, kKey5.
-  base::flat_set<CacheEntryKey> excluded_keys = {kKey2};
-  ASSERT_EQ(DeleteLiveEntriesBetween(kTime1, kTime3, excluded_keys),
+  base::flat_set<SqlPersistentStore::ResId> excluded_ids = {res_id2};
+  ASSERT_EQ(DeleteLiveEntriesBetween(kTime1, kTime3, excluded_ids),
             SqlPersistentStore::Error::kOk);
   EXPECT_EQ(store_->GetIndexStateForHash(kKey1.hash()),
             SqlPersistentStore::IndexState::kHashNotFound);
@@ -3603,12 +3576,16 @@
 
   // Add entries until size > high watermark.
   std::vector<CacheEntryKey> keys;
+  std::optional<SqlPersistentStore::ResId> first_res_id;
   int i = 0;
   while (GetSizeOfAllEntries() <= kHighWatermark) {
     const CacheEntryKey key(base::StringPrintf("key%d", i++));
     keys.push_back(key);
     auto create_result = CreateEntry(key);
     ASSERT_TRUE(create_result.has_value());
+    if (!first_res_id.has_value()) {
+      first_res_id = create_result->res_id;
+    }
     task_environment_.AdvanceClock(
         base::Seconds(1));  // To distinguish last_used
   }
@@ -3619,11 +3596,11 @@
   EXPECT_TRUE(store_->ShouldStartEviction());
 
   // Exclude the oldest entry.
-  base::flat_set<CacheEntryKey> excluded_keys = {keys[0]};
+  base::flat_set<SqlPersistentStore::ResId> excluded_res_ids = {*first_res_id};
 
   // Start eviction.
   base::test::TestFuture<SqlPersistentStore::Error> future;
-  store_->StartEviction(std::move(excluded_keys), future.GetCallback());
+  store_->StartEviction(std::move(excluded_res_ids), future.GetCallback());
   ASSERT_EQ(future.Get(), SqlPersistentStore::Error::kOk);
 
   // After eviction, size should be <= low watermark.
@@ -3968,9 +3945,6 @@
   EXPECT_EQ(DeleteDoomedEntry(kKey, SqlPersistentStore::ResId(1)),
             SqlPersistentStore::Error::kFailedForTesting);
 
-  EXPECT_EQ(DeleteDoomedEntries({}),
-            SqlPersistentStore::Error::kFailedForTesting);
-
   EXPECT_EQ(DeleteLiveEntry(kKey),
             SqlPersistentStore::Error::kFailedForTesting);
 
diff --git a/net/http/OWNERS b/net/http/OWNERS
index 37e74ce..3b14a6f 100644
--- a/net/http/OWNERS
+++ b/net/http/OWNERS
@@ -1,7 +1,8 @@
 per-file http_cache_*=shivanisha@chromium.org
-per-file transport_security_state_static.*=estark@chromium.org
-per-file transport_security_state_static.*=cthomp@chromium.org
-per-file transport_security_state_static.*=jdeblasio@chromium.org
+per-file transport_security_state_static*=estark@chromium.org
+per-file transport_security_state_static*=cthomp@chromium.org
+per-file transport_security_state_static*=carlosil@chromium.org
+per-file transport_security_state_static*=jdeblasio@chromium.org
 # For automated updates
 per-file transport_security_state_static.*=mdb.chrome-pki-metadata-release-jobs@google.com
 per-file transport_security_state_static_pins.json=mdb.chrome-pki-metadata-release-jobs@google.com
diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc
index 4867b33b..ec1799e 100644
--- a/net/http/http_response_info.cc
+++ b/net/http/http_response_info.cc
@@ -138,8 +138,8 @@
 // These values can be bit-wise combined to form the extra flags field of the
 // serialized HttpResponseInfo.
 enum {
-  // This bit is set if the request usd a shared dictionary for decoding its
-  // body.
+  // This bit was set if the request used a shared dictionary for decoding its
+  // body but is no longer persisted.
   RESPONSE_EXTRA_INFO_DID_USE_SHARED_DICTIONARY = 1,
 
   // This bit is set if the response has valid `proxy_chain`.
@@ -366,8 +366,10 @@
     browser_run_id = std::make_optional(id);
   }
 
-  did_use_shared_dictionary =
-      (extra_flags & RESPONSE_EXTRA_INFO_DID_USE_SHARED_DICTIONARY) != 0;
+  // Do NOT restore the did_use_shared_dictionary flag since
+  // dictionary-compressed responses are decoded before being stored in cache.
+  // It is no longer persisted but old cache entries may have it set.
+  did_use_shared_dictionary = false;
 
   if (extra_flags & RESPONSE_EXTRA_INFO_HAS_PROXY_CHAIN) {
     std::optional<ProxyChain> unpickled_proxy_chain =
@@ -435,10 +437,6 @@
   if (browser_run_id.has_value())
     flags |= RESPONSE_INFO_BROWSER_RUN_ID;
 
-  if (did_use_shared_dictionary) {
-    extra_flags |= RESPONSE_EXTRA_INFO_DID_USE_SHARED_DICTIONARY;
-  }
-
   if (proxy_chain.IsValid()) {
     extra_flags |= RESPONSE_EXTRA_INFO_HAS_PROXY_CHAIN;
   }
diff --git a/net/http/http_response_info.h b/net/http/http_response_info.h
index 10b9a707..085af3ff 100644
--- a/net/http/http_response_info.h
+++ b/net/http/http_response_info.h
@@ -204,6 +204,8 @@
   std::optional<int64_t> browser_run_id;
 
   // True if the response used a shared dictionary for decoding its body.
+  // This is always false for resources served from cache (where
+  // dictionary-compressed responses are stored uncompressed).
   bool did_use_shared_dictionary = false;
 };
 
diff --git a/net/http/http_response_info_unittest.cc b/net/http/http_response_info_unittest.cc
index f01c66f0..5fc30142 100644
--- a/net/http/http_response_info_unittest.cc
+++ b/net/http/http_response_info_unittest.cc
@@ -308,12 +308,12 @@
   EXPECT_FALSE(restored_response_info.browser_run_id.has_value());
 }
 
-// Test that did_use_shared_dictionary is preserved .
+// Test that did_use_shared_dictionary is NOT preserved .
 TEST_F(HttpResponseInfoTest, DidUseSharedDictionary) {
   response_info_.did_use_shared_dictionary = true;
   HttpResponseInfo restored_response_info;
   PickleAndRestore(response_info_, &restored_response_info);
-  EXPECT_TRUE(restored_response_info.did_use_shared_dictionary);
+  EXPECT_FALSE(restored_response_info.did_use_shared_dictionary);
 }
 
 }  // namespace
diff --git a/net/http/http_stream_pool_attempt_manager.cc b/net/http/http_stream_pool_attempt_manager.cc
index 6519d0e5..951e1ec 100644
--- a/net/http/http_stream_pool_attempt_manager.cc
+++ b/net/http/http_stream_pool_attempt_manager.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <map>
 #include <memory>
+#include <string_view>
 #include <utility>
 
 #include "base/containers/contains.h"
@@ -556,7 +557,20 @@
   MaybeCompleteLater();
 }
 
-void HttpStreamPool::AttemptManager::CancelJobs(int error) {
+void HttpStreamPool::AttemptManager::CancelJobs(
+    int error,
+    StreamSocketCloseReason cancel_reason) {
+  std::string_view reason_suffix =
+      StreamSocketCloseReasonToString(cancel_reason);
+  base::UmaHistogramCounts100(
+      base::StrCat(
+          {"Net.HttpStreamPool.RequestJobCancelCount.", reason_suffix}),
+      request_jobs_.size());
+  base::UmaHistogramCounts100(
+      base::StrCat(
+          {"Net.HttpStreamPool.PreconnectJobCancelCount.", reason_suffix}),
+      preconnect_jobs_.size());
+
   HandleFinalError(error);
 }
 
diff --git a/net/http/http_stream_pool_attempt_manager.h b/net/http/http_stream_pool_attempt_manager.h
index befa945..c7a37ea 100644
--- a/net/http/http_stream_pool_attempt_manager.h
+++ b/net/http/http_stream_pool_attempt_manager.h
@@ -191,7 +191,7 @@
   void OnJobComplete(Job* job);
 
   // Cancels all jobs.
-  void CancelJobs(int error);
+  void CancelJobs(int error, StreamSocketCloseReason cancel_reason);
 
   // Cancels the QuicAttempt if it exists.
   void CancelQuicAttempt(int error);
diff --git a/net/http/http_stream_pool_group.cc b/net/http/http_stream_pool_group.cc
index 2ce7086..f28704d3 100644
--- a/net/http/http_stream_pool_group.cc
+++ b/net/http/http_stream_pool_group.cc
@@ -292,7 +292,7 @@
     StreamSocketCloseReason attempt_cancel_reason,
     std::string_view net_log_close_reason_utf8) {
   Refresh(net_log_close_reason_utf8, attempt_cancel_reason);
-  CancelJobs(error);
+  CancelJobs(error, attempt_cancel_reason);
 }
 
 void HttpStreamPool::Group::Refresh(std::string_view net_log_close_reason_utf8,
@@ -309,9 +309,10 @@
   CleanupIdleStreamSockets(CleanupMode::kForce, net_log_close_reason_utf8);
 }
 
-void HttpStreamPool::Group::CancelJobs(int error) {
+void HttpStreamPool::Group::CancelJobs(int error,
+                                       StreamSocketCloseReason cancel_reason) {
   if (attempt_manager_) {
-    attempt_manager_->CancelJobs(error);
+    attempt_manager_->CancelJobs(error, cancel_reason);
   }
 }
 
diff --git a/net/http/http_stream_pool_group.h b/net/http/http_stream_pool_group.h
index 71e7fdc..51f960d 100644
--- a/net/http/http_stream_pool_group.h
+++ b/net/http/http_stream_pool_group.h
@@ -173,7 +173,7 @@
   void CloseIdleStreams(std::string_view net_log_close_reason_utf8);
 
   // Cancels all on-going jobs.
-  void CancelJobs(int error);
+  void CancelJobs(int error, StreamSocketCloseReason cancel_reason);
 
   // Returns an active AttemptManager for `job`.
   AttemptManager* GetAttemptManagerForJob(Job* job);
diff --git a/net/http/transport_security_state_static.pins b/net/http/transport_security_state_static.pins
index eeddddad..c2d8082 100644
--- a/net/http/transport_security_state_static.pins
+++ b/net/http/transport_security_state_static.pins
@@ -43,9 +43,9 @@
 #   hash function for preloaded entries again (we have already done so once).
 #
 
-# Last updated: 2025-09-09 12:53 UTC
+# Last updated: 2025-10-06 12:58 UTC
 PinsListTimestamp
-1757422428
+1759755517
 
 TestSPKI
 sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
@@ -439,78 +439,6 @@
 bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm
 -----END CERTIFICATE-----
 
-DigiCertEVRoot
------BEGIN CERTIFICATE-----
-MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
-ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
-MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
-LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
-RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
-+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
-PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
-xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
-Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
-hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
-EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
-MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
-FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
-nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
-eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
-hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
-Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
-vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
-+OkuE6N36B9K
------END CERTIFICATE-----
-
-DigiCertGlobalRoot
------BEGIN CERTIFICATE-----
-MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
-QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
-MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
-b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
-9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
-CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
-nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
-43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
-T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
-gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
-BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
-TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
-DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
-hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
-06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
-PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
-YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
-CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
------END CERTIFICATE-----
-
-GlobalSignRootCA
------BEGIN CERTIFICATE-----
-MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
-A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
-b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
-MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
-YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
-aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
-jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
-xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
-1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
-snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
-U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
-9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
-BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
-AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
-yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
-38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
-AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
-DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
-HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
------END CERTIFICATE-----
-
 GlobalSignExtendedValidationCA
 -----BEGIN CERTIFICATE-----
 MIIEmDCCA4CgAwIBAgILBAAAAAABIg08FMUwDQYJKoZIhvcNAQEFBQAwTDEgMB4G
@@ -597,458 +525,4 @@
 iyRRrl3tovG7UxBNl/oadwM=
 -----END CERTIFICATE-----
 
-GlobalSignRootCA_R3
------BEGIN CERTIFICATE-----
-MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
-A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
-Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
-MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
-A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
-RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
-gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
-KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
-QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
-XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
-DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
-LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
-RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
-jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
-6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
-mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
-Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
-WD9f
------END CERTIFICATE-----
-
-GlobalSignRootCA_R6
------BEGIN CERTIFICATE-----
-MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDE
-gMB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2
-JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNM
-zQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBS
-NjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiI
-wDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQ
-ssgrRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuT
-ToVBu1kZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSK
-vGRMIRxDaNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n
-16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9
-CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJ
-Da38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiW
-m05OWgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4
-UoQSwC+n+7o/hbguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQ
-Ce24DWJfncBZ4nWUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFl
-WQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZ
-cIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjA
-PBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToD
-AfBgNVHSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFA
-AOCAgEAgyXt6NH9lVLNnsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcW
-c+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKP
-rmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waN
-rlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944
-Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl
-+68KnyBr3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU
-3/gKbaKxCXcPu9czc8FB10jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTO
-wY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsVi
-VO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9
-x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDf
-LRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA=
------END CERTIFICATE-----
-
-
-GlobalSignRootR46
------BEGIN CERTIFICATE-----
-MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAU
-AMEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGg
-YDVQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2M
-DMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24g
-bnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb
-3DQEBAQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08
-EsCVeJOaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUl
-ghYruQGvGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTq
-a1VbkNud316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/O
-rffGFqfUo0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPT
-Xhc+CtI4wSEy132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTy
-G/FWaha/LIWFzXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0N
-XfeD412lPFzYE+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JM
-WKmIJ5jqSngiCNI/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+F
-fy7dXxd7Pj2Fxzsx2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7
-/mrLZqrcZdCinkqaByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKg
-Gwvgqo6GDoLclcqUC4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQ
-H/BAUwAwEB/zAdBgNVHQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIh
-vcNAQEMBQADggIBAHx47PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048
-p9gkUbJUHJNOxO97k4VgJuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63b
-EIaZHU1VNaL8FpO7XJqti2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6
-Fcg0XERczzF2sUyQQCPtIkpnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MV
-enQTqnMdzGxRBF6XLE+0xRFFRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdk
-LG5NrmJ7v2B0GbhWrJKsFjLtrWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSy
-BQ7N0H3qqJZ4d16GLuc1CLgSkZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7f
-XwgNNgyYMqIgXQBztSvwyeqiv5u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJ
-Mbfc2hIkCwU9D9SGuTSyxTDYWnP4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvd
-kzrnE6JwYZ+vj+vYxXX4M2bUdGc6N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE
-9dJeFt0g5QdYg/NA6s/rob8SKunE3vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QE
-UxeCp6
------END CERTIFICATE-----
-
-
-SymantecClass3EVG3
------BEGIN CERTIFICATE-----
-MIIFKzCCBBOgAwIBAgIQfuFKb2/v8tN/P61lTTratDANBgkqhkiG9w0BAQsFADCB
-yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
-ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
-U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
-ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
-aG9yaXR5IC0gRzUwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB3MQsw
-CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV
-BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5bWFudGVjIENs
-YXNzIDMgRVYgU1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
-AoIBAQDYoWV0I+grZOIy1zM3PY71NBZI3U9/hxz4RCMTjvsR2ERaGHGOYBYmkpv9
-FwvhcXBC/r/6HMCqo6e1cej/GIP23xAKE2LIPZyn3i4/DNkd5y77Ks7Imn+Hv9hM
-BBUyydHMlXGgTihPhNk1++OGb5RT5nKKY2cuvmn2926OnGAE6yn6xEdC0niY4+wL
-pZLct5q9gGQrOHw4CVtm9i2VeoayNC6FnpAOX7ddpFFyRnATv2fytqdNFB5suVPu
-IxpOjUhVQ0GxiXVqQCjFfd3SbtICGS97JJRL6/EaqZvjI5rq+jOrCiy39GAI3Z8c
-zd0tAWaAr7MvKR0juIrhoXAHDDQPAgMBAAGjggFdMIIBWTAvBggrBgEFBQcBAQQj
-MCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9zMi5zeW1jYi5jb20wEgYDVR0TAQH/BAgw
-BgEB/wIBADBlBgNVHSAEXjBcMFoGBFUdIAAwUjAmBggrBgEFBQcCARYaaHR0cDov
-L3d3dy5zeW1hdXRoLmNvbS9jcHMwKAYIKwYBBQUHAgIwHBoaaHR0cDovL3d3dy5z
-eW1hdXRoLmNvbS9ycGEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3MxLnN5bWNi
-LmNvbS9wY2EzLWc1LmNybDAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0RBCIwIKQeMBwx
-GjAYBgNVBAMTEVN5bWFudGVjUEtJLTEtNTMzMB0GA1UdDgQWBBQBWavn3ToLWaZk
-Y9bPIAdX1ZHnajAfBgNVHSMEGDAWgBR/02Wnwt3su/AwCfNDOfoCrzMxMzANBgkq
-hkiG9w0BAQsFAAOCAQEAQgFVe9AWGl1Y6LubqE3X89frE5SG1n8hC0e8V5uSXU8F
-nzikEHzPg74GQ0aNCLxq1xCm+quvL2GoY/Jl339MiBKIT7Np2f8nwAqXkY9W+4nE
-qLuSLRtzsMarNvSWbCAI7woeZiRFT2cAQMgHVHQzO6atuyOfZu2iRHA0+w7qAf3P
-eHTfp61Vt19N9tY/4IbOJMdCqRMURDVLtt/JYKwMf9mTIUvunORJApjTYHtcvNUw
-LwfORELEC5n+5p/8sHiGUW3RLJ3GlvuFgrsEL/digO9i2n/2DqyQuFa9eT/ygG6j
-2bkPXToHHZGThkspTOHcteHgM52zyzaRS/6htO7w+Q==
------END CERTIFICATE-----
-
-DigiCertECCSecureServerCA
------BEGIN CERTIFICATE-----
-MIIDrDCCApSgAwIBAgIQCssoukZe5TkIdnRw883GEjANBgkqhkiG9w0BAQwFADBh
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
-QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEwxCzAJBgNVBAYTAlVT
-MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJjAkBgNVBAMTHURpZ2lDZXJ0IEVDQyBT
-ZWN1cmUgU2VydmVyIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4ghC6nfYJN6g
-LGSkE85AnCNyqQIKDjc/ITa4jVMU9tWRlUvzlgKNcR7E2Munn17voOZ/WpIRllNv
-68DLP679Wz9HJOeaBy6Wvqgvu1cYr3GkvXg6HuhbPGtkESvMNCuMo4IBITCCAR0w
-EgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwNAYIKwYBBQUHAQEE
-KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQgYDVR0f
-BDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xv
-YmFsUm9vdENBLmNybDA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYc
-aHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAdBgNVHQ4EFgQUo53mH/naOU/A
-buiRy5Wl2jHiCp8wHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJ
-KoZIhvcNAQEMBQADggEBAMeKoENL7HTJxavVHzA1Nm6YVntIrAVjrnuaVyRXzG/6
-3qttnMe2uuzO58pzZNvfBDcKAEmzP58mrZGMIOgfiA4q+2Y3yDDo0sIkp0VILeoB
-UEoxlBPfjV/aKrtJPGHzecicZpIalir0ezZYoyxBEHQa0+1IttK7igZFcTMQMHp6
-mCHdJLnsnLWSB62DxsRq+HfmNb4TDydkskO/g+l3VtsIh5RHFPVfKK+jaEyDj2D3
-loB5hWp2Jp2VDCADjT7ueihlZGak2YPqmXTNbk19HOuNssWvFhtOyPNV6og4ETQd
-Ea8/B6hPatJ0ES8q/HO3X8IVQwVs1n3aAr0im0/T+Xc=
------END CERTIFICATE-----
-
-FacebookBackup
------BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtHIqUt4FvcdEZQZqucdY
-UEWSMuSIZnRgs0vvyLqb0KmIz8n8WLwifrMzTnrk9ol8EMcuoe4kkntJ3kn4pRIm
-2Sdw33nibWBzXJu3zuorFGFZ9fsZLfh1zHIrNAKbjQmjHnD+XJLfPyoRI/3eOcl4
-ArQwdqg5ymAmffy8Zapgrf3tSa6OsOxZF7+dLVfr+zzh2tawfH+kzzS3e0eXyO1x
-aSSpcSsFjuB1/cBJDeS/0a1/eLK8KeCWZcb7ev7ge0WnRvkfo+0KxdzSFxTrfjg0
-uy8blcsjtFoq5lG5Ba1982Qzkmot+08ZZQi/QxZ+QARS16YL3KQgJRCb7y+UXmGa
-PQIDAQAB
------END PUBLIC KEY-----
-
-ISRGRootX1
------BEGIN CERTIFICATE-----
-MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
-TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
-cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
-WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
-ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
-MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
-h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
-0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
-A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
-T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
-B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
-B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
-KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
-OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
-jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
-qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
-rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
-HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
-hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
-ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
-3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
-NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
-ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
-TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
-jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
-oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
-4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
-mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
-emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
------END CERTIFICATE-----
-
-
-ISRGRootX2
------BEGIN CERTIFICATE-----
-MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
-CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
-R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
-MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT
-ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw
-EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW
-+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9
-ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
-AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI
-zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
-tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
-/q4AaOeMSQ+2b1tbFfLn
------END CERTIFICATE-----
-
-
-# DigiCert Global Root G2
-# https://www.digicert.com/CACerts/DigiCertGlobalRootG2.crt
-DigiCertGlobalRootG2
------BEGIN CERTIFICATE-----
-MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
-MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
-MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
-b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
-9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
-2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
-1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
-q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
-tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
-vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
-BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
-5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
-1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
-NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
-Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
-8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
-pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
-MrY=
------END CERTIFICATE-----
-
-# DigiCert Global Root G3
-# https://www.digicert.com/CACerts/DigiCertGlobalRootG3.crt
-DigiCertGlobalRootG3
------BEGIN CERTIFICATE-----
-MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw
-CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
-ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe
-Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw
-EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
-IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF
-K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG
-fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO
-Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd
-BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx
-AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/
-oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8
-sycX
------END CERTIFICATE-----
-
-# DigiCert Trusted Root G4
-# https://www.digicert.com/CACerts/DigiCertTrustedRootG4.crt
-DigiCertTrustedRootG4
------BEGIN CERTIFICATE-----
-MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg
-RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV
-UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
-Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG
-SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y
-ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If
-xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV
-ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO
-DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ
-jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/
-CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi
-EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM
-fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY
-uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK
-chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t
-9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
-hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD
-ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2
-SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd
-+SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc
-fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa
-sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N
-cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N
-0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie
-4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI
-r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1
-/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm
-gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+
------END CERTIFICATE-----
-
-DigiCertTLSRSA4096RootG5
------BEGIN CERTIFICATE-----
-MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN
-MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT
-HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN
-NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs
-IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi
-MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+
-ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0
-2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp
-wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM
-pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD
-nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po
-sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx
-Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd
-Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX
-KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe
-XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL
-tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv
-TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN
-AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw
-GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H
-PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF
-O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ
-REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik
-AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv
-/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+
-p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw
-MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF
-qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK
-ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+
------END CERTIFICATE-----
-
-
-DigiCertTLSECCP384RootG5
------BEGIN CERTIFICATE-----
-MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw
-CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp
-Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2
-MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
-bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG
-ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS
-7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp
-0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS
-B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49
-BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ
-LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4
-DXZDjC5Ty3zfDBeWUA==
------END CERTIFICATE-----
-
-
-UserTrustRSACertificationAuthority
------BEGIN CERTIFICATE-----
-MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
-iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
-cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
-BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
-MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
-BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
-aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
-dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
-AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
-3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
-tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
-Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
-VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
-79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
-c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
-Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
-c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
-UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
-Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
-BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
-A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
-Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
-VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
-ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
-8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
-iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
-Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
-XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
-qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
-VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
-L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
-jjxDah2nGN59PRbxYvnKkKj9
------END CERTIFICATE-----
-
-
-UserTrustECCCertificationAuthority
------BEGIN CERTIFICATE-----
-MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL
-MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl
-eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT
-JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx
-MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
-Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg
-VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm
-aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo
-I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng
-o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G
-A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD
-VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB
-zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW
-RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
------END CERTIFICATE-----
-
-
-ComodoRSACertificationAuthority
------BEGIN CERTIFICATE-----
-MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB
-hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
-A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
-BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5
-MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
-EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
-Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh
-dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR
-6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X
-pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC
-9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV
-/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf
-Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z
-+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w
-qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah
-SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC
-u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf
-Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq
-crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
-FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
-/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl
-wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM
-4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV
-2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna
-FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ
-CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK
-boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke
-jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL
-S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb
-QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl
-0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB
-NVOFBkpdn627G190
------END CERTIFICATE-----
-
-
-ComodoECCCertificationAuthority
------BEGIN CERTIFICATE-----
-MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
-MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
-BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
-IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw
-MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
-ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
-T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv
-biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR
-FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J
-cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW
-BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
-BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm
-fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
-GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
------END CERTIFICATE-----
-
 
diff --git a/net/http/transport_security_state_static_pins.json b/net/http/transport_security_state_static_pins.json
index cad5087..f1232c7 100644
--- a/net/http/transport_security_state_static_pins.json
+++ b/net/http/transport_security_state_static_pins.json
@@ -31,7 +31,7 @@
 // the 'static_spki_hashes' and 'bad_static_spki_hashes' fields in 'pinsets'
 // refer to, and the timestamp at which the pins list was last updated.
 //
-// Last updated: 2025-09-09 12:53 UTC
+// Last updated: 2025-10-06 12:58 UTC
 //
 {
   "pinsets": [
@@ -67,31 +67,6 @@
         "GlobalSignExtendedValidationCA_G2",
         "GlobalSignExtendedValidationCA_SHA256_G2"
       ]
-    },
-    {
-      "name": "facebook",
-      "static_spki_hashes": [
-        "ComodoRSACertificationAuthority",
-        "ComodoECCCertificationAuthority",
-        "UserTrustRSACertificationAuthority",
-        "UserTrustECCCertificationAuthority",
-        "DigiCertGlobalRoot",
-        "DigiCertGlobalRootG2",
-        "DigiCertGlobalRootG3",
-        "DigiCertEVRoot",
-        "DigiCertTrustedRootG4",
-        "DigiCertTLSRSA4096RootG5",
-        "DigiCertTLSECCP384RootG5",
-        "GlobalSignRootCA",
-        "GlobalSignRootCA_R3",
-        "GlobalSignRootCA_R6",
-        "GlobalSignRootR46",
-        "ISRGRootX1",
-        "ISRGRootX2",
-        "FacebookBackup",
-        "SymantecClass3EVG3",
-        "DigiCertECCSecureServerCA"
-      ]
     }
   ],
   "entries": [
@@ -1909,96 +1884,6 @@
       "name": "ytimg.com",
       "include_subdomains": true,
       "pins": "google"
-    },
-    {
-      "name": "facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "www.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "m.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "tablet.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "secure.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "pixel.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "apps.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "upload.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "developers.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "touch.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "mbasic.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "code.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "t.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "mtouch.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "business.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "research.facebook.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "messenger.com",
-      "include_subdomains": true,
-      "pins": "facebook"
-    },
-    {
-      "name": "www.messenger.com",
-      "include_subdomains": true,
-      "pins": "facebook"
     }
   ]
 }
\ No newline at end of file
diff --git a/net/http/transport_security_state_unittest.cc b/net/http/transport_security_state_unittest.cc
index b786f0ef..9386aa0 100644
--- a/net/http/transport_security_state_unittest.cc
+++ b/net/http/transport_security_state_unittest.cc
@@ -1446,23 +1446,17 @@
   EXPECT_TRUE(OnlyPinningInStaticState("doubleclick.net"));
   EXPECT_TRUE(OnlyPinningInStaticState("googlegroups.com"));
 
-  // Facebook has pinning and hsts on facebook.com, but only pinning on
-  // subdomains.
-  EXPECT_TRUE(state.GetStaticPKPState("facebook.com", &pkp_state));
-  EXPECT_FALSE(pkp_state.spki_hashes.empty());
+  // Facebook is not pinned but has hsts only on facebook.com.
+  EXPECT_FALSE(state.GetStaticPKPState("facebook.com", &pkp_state));
   EXPECT_TRUE(StaticShouldRedirect("facebook.com"));
-
-  EXPECT_TRUE(state.GetStaticPKPState("foo.facebook.com", &pkp_state));
-  EXPECT_FALSE(pkp_state.spki_hashes.empty());
+  EXPECT_FALSE(state.GetStaticPKPState("foo.facebook.com", &pkp_state));
   EXPECT_FALSE(StaticShouldRedirect("foo.facebook.com"));
 
-  // www.facebook.com and subdomains have both pinning and hsts.
-  EXPECT_TRUE(state.GetStaticPKPState("www.facebook.com", &pkp_state));
-  EXPECT_FALSE(pkp_state.spki_hashes.empty());
+  // www.facebook.com and subdomains are not pinned, but do have hsts.
+  EXPECT_FALSE(state.GetStaticPKPState("www.facebook.com", &pkp_state));
   EXPECT_TRUE(StaticShouldRedirect("www.facebook.com"));
 
-  EXPECT_TRUE(state.GetStaticPKPState("foo.www.facebook.com", &pkp_state));
-  EXPECT_FALSE(pkp_state.spki_hashes.empty());
+  EXPECT_FALSE(state.GetStaticPKPState("foo.www.facebook.com", &pkp_state));
   EXPECT_TRUE(StaticShouldRedirect("foo.www.facebook.com"));
 }
 
diff --git a/net/socket/stream_socket_close_reason.h b/net/socket/stream_socket_close_reason.h
index 58b90cb..43c14d3 100644
--- a/net/socket/stream_socket_close_reason.h
+++ b/net/socket/stream_socket_close_reason.h
@@ -28,7 +28,7 @@
   kAttemptManagerDraining = 10,
   kMaxValue = kAttemptManagerDraining,
 };
-// LINT.ThenChange(//tools/metrics/histograms/metadata/net/enums.xml:StreamSocketCloseReason)
+// LINT.ThenChange(//tools/metrics/histograms/metadata/net/enums.xml:StreamSocketCloseReason,tools/metrics/histograms/metadata/net/histograms.xml:StreamSocketCloseReason)
 
 std::string_view StreamSocketCloseReasonToString(
     StreamSocketCloseReason reason);
diff --git a/pdf/pdf_caret.cc b/pdf/pdf_caret.cc
index e2b377fe..bd258f4 100644
--- a/pdf/pdf_caret.cc
+++ b/pdf/pdf_caret.cc
@@ -38,12 +38,21 @@
 
 PdfCaret::~PdfCaret() = default;
 
-void PdfCaret::SetVisibility(bool is_visible) {
-  if (is_visible_ == is_visible) {
+void PdfCaret::SetEnabled(bool enabled) {
+  if (enabled_ == enabled) {
     return;
   }
 
-  is_visible_ = is_visible;
+  enabled_ = enabled;
+  RefreshDisplayState();
+}
+
+void PdfCaret::SetVisible(bool visible) {
+  if (is_visible_ == visible) {
+    return;
+  }
+
+  is_visible_ = visible;
   RefreshDisplayState();
 }
 
@@ -72,7 +81,7 @@
 
 void PdfCaret::SetCharAndDraw(const PageCharacterIndex& next_char) {
   SetChar(next_char);
-  if (is_visible_) {
+  if (ShouldDrawCaret()) {
     RefreshDisplayState();
   }
 }
@@ -83,10 +92,6 @@
     return false;
   }
 
-  if (client_->IsSelecting()) {
-    return false;
-  }
-
   gfx::Rect visible_caret =
       gfx::IntersectRects(caret_screen_rect_, dirty_in_screen);
   if (visible_caret.IsEmpty()) {
@@ -99,7 +104,7 @@
 }
 
 void PdfCaret::OnGeometryChanged() {
-  if (!is_visible_) {
+  if (!ShouldDrawCaret()) {
     return;
   }
 
@@ -130,20 +135,24 @@
   }
 }
 
+bool PdfCaret::ShouldDrawCaret() const {
+  return enabled_ && is_visible_;
+}
+
 void PdfCaret::RefreshDisplayState() {
   blink_timer_.Stop();
-  if (is_visible_ && blink_interval_.is_positive()) {
+  is_blink_visible_ = ShouldDrawCaret();
+  if (is_blink_visible_ && blink_interval_.is_positive()) {
     blink_timer_.Start(FROM_HERE, blink_interval_, this,
                        &PdfCaret::OnBlinkTimerFired);
   }
-  is_blink_visible_ = is_visible_;
   if (!caret_screen_rect_.IsEmpty()) {
     client_->InvalidateRect(caret_screen_rect_);
   }
 }
 
 void PdfCaret::OnBlinkTimerFired() {
-  CHECK(is_visible_);
+  CHECK(ShouldDrawCaret());
   CHECK(blink_interval_.is_positive());
   is_blink_visible_ = !is_blink_visible_;
   if (!caret_screen_rect_.IsEmpty()) {
diff --git a/pdf/pdf_caret.h b/pdf/pdf_caret.h
index 55a55b5..09b8353 100644
--- a/pdf/pdf_caret.h
+++ b/pdf/pdf_caret.h
@@ -47,9 +47,18 @@
   PdfCaret& operator=(const PdfCaret&) = delete;
   ~PdfCaret();
 
-  // Sets the visibility of the caret. No-op if visibility does not change. If
-  // `is_visible` is true, the caret will be drawn, hidden otherwise.
-  void SetVisibility(bool is_visible);
+  // Sets whether the caret is enabled. No-op if state does not change. Draws
+  // the caret if it should be visible, hides it otherwise. See
+  // `ShouldDrawCaret()` for when the caret will be visible.
+  void SetEnabled(bool enabled);
+
+  // Sets whether the caret should be visible. No-op if state does not change.
+  // Draws the caret if it should be visible, hides it otherwise. Note that even
+  // if `visible` is true, the caret may still be hidden if the caret is
+  // disabled. This state can be desired if the caller wants the caret to be
+  // visible again on re-enable. See `ShouldDrawCaret()` for when the caret will
+  // be visible.
+  void SetVisible(bool visible);
 
   // Sets how often the caret should blink. If the interval is set to 0, the
   // caret will not blink. No-op if `interval` is negative.
@@ -68,8 +77,7 @@
   void SetCharAndDraw(const PageCharacterIndex& next_char);
 
   // Draws the caret on the canvas if it is visible within any paint updates in
-  // `dirty_in_screen` and no text is selected. Returns true if the caret was
-  // drawn, false otherwise.
+  // `dirty_in_screen`. Returns true if the caret was drawn, false otherwise.
   bool MaybeDrawCaret(const RegionData& region,
                       const gfx::Rect& dirty_in_screen) const;
 
@@ -82,9 +90,13 @@
   bool OnKeyDown(const blink::WebKeyboardEvent& event);
 
  private:
+  // Returns whether the caret should be drawn. It should only be drawn when the
+  // caret is enabled and set as visible.
+  bool ShouldDrawCaret() const;
+
   // Refreshes the caret's display state, drawing or hiding the caret depending
-  // on the value of `is_visible_` and resetting the blink timer depending on
-  // the value of `is_blinking_`.
+  // on the value of `ShouldDrawCaret()` and resetting the blink timer depending
+  // on the value of `is_blinking_`.
   void RefreshDisplayState();
 
   // Called by `blink_timer_` to toggle caret visibility.
@@ -177,6 +189,9 @@
   // to the right of the last char.
   PageCharacterIndex index_;
 
+  // Whether the caret is enabled.
+  bool enabled_ = false;
+
   // Whether the caret is visible.
   bool is_visible_ = false;
 
diff --git a/pdf/pdf_caret_unittest.cc b/pdf/pdf_caret_unittest.cc
index 6f8ee6da..8d1bce0 100644
--- a/pdf/pdf_caret_unittest.cc
+++ b/pdf/pdf_caret_unittest.cc
@@ -120,6 +120,7 @@
 
   void InitializeCaretAtChar(const PageCharacterIndex& index) {
     caret_ = std::make_unique<PdfCaret>(&client_, index);
+    caret_->SetVisible(true);
   }
 
   RegionData GetRegionData(const gfx::Point& location) {
@@ -245,25 +246,49 @@
   SetUpChar(kTestChar0, '\0', {kDefaultCaret});
   InitializeCaretAtChar(kTestChar0);
 
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   TestDrawCaret(kDefaultCaret);
 }
 
-TEST_F(PdfCaretTest, SetVisibility) {
+TEST_F(PdfCaretTest, SetEnabled) {
   SetUpPagesWithCharCounts({1});
   SetUpChar(kTestChar0, 'a', {kTestChar0ScreenRect});
   InitializeCaretAtChar(kTestChar0);
 
-  caret().SetVisibility(false);
+  caret().SetEnabled(false);
 
   TestDrawCaretFails(kTestChar0Caret);
 
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   TestDrawCaret(kTestChar0Caret);
 
-  caret().SetVisibility(false);
+  caret().SetEnabled(false);
+  TestDrawCaretFails(kTestChar0Caret);
+
+  GetPdfTestTaskEnvironment().FastForwardBy(PdfCaret::kDefaultBlinkInterval);
+  TestDrawCaretFails(kTestChar0Caret);
+}
+
+TEST_F(PdfCaretTest, SetVisible) {
+  SetUpPagesWithCharCounts({1});
+  SetUpChar(kTestChar0, 'a', {kTestChar0ScreenRect});
+  InitializeCaretAtChar(kTestChar0);
+
+  caret().SetEnabled(true);
+
+  TestDrawCaret(kTestChar0Caret);
+
+  caret().SetVisible(false);
+
+  TestDrawCaretFails(kTestChar0Caret);
+
+  caret().SetVisible(true);
+
+  TestDrawCaret(kTestChar0Caret);
+
+  caret().SetVisible(false);
   TestDrawCaretFails(kTestChar0Caret);
 
   GetPdfTestTaskEnvironment().FastForwardBy(PdfCaret::kDefaultBlinkInterval);
@@ -275,7 +300,7 @@
   SetUpChar(kTestChar0, 'a', {kTestChar0ScreenRect});
   InitializeCaretAtChar(kTestChar0);
 
-  caret().SetVisibility(false);
+  caret().SetEnabled(false);
   TestDrawCaretFails(kTestChar0Caret);
 
   // Blinks by default, but not visible.
@@ -305,7 +330,7 @@
   SetUpChar(kTestChar0, 'a', {kTestChar0ScreenRect});
   InitializeCaretAtChar(kTestChar0);
 
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   TestDrawCaret(kTestChar0Caret);
 
@@ -336,7 +361,7 @@
   SetUpChar(kTestChar0, 'a', {kTestChar0ScreenRect});
   InitializeCaretAtChar(kTestChar0);
 
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Setting blink interval to negative does nothing.
   caret().SetBlinkInterval(base::Milliseconds(-100));
@@ -359,7 +384,7 @@
   EXPECT_FALSE(caret().MaybeDrawCaret(GetRegionData(kTestChar0Caret.origin()),
                                       kTestChar0Caret));
 
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Not dirty in screen.
   EXPECT_FALSE(caret().MaybeDrawCaret(GetRegionData(gfx::Point(70, 70)),
@@ -378,25 +403,12 @@
   VerifyCaretRendering(kTestChar0Caret);
 }
 
-TEST_F(PdfCaretTest, CaretNotVisibleWhileSelecting) {
-  SetUpPagesWithCharCounts({1});
-  SetUpChar(kTestChar0, 'a', {kTestChar0ScreenRect});
-  InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
-
-  EXPECT_CALL(client(), IsSelecting()).WillOnce(Return(true));
-  TestDrawCaretFails(kTestChar0Caret);
-
-  EXPECT_CALL(client(), IsSelecting()).WillOnce(Return(false));
-  TestDrawCaret(kTestChar0Caret);
-}
-
 TEST_F(PdfCaretTest, Blink) {
   SetUpPagesWithCharCounts({2});
   SetUpChar(kTestChar0, 'a', {kTestChar0ScreenRect});
   InitializeCaretAtChar(kTestChar0);
 
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
   TestDrawCaret(kTestChar0Caret);
 
   GetPdfTestTaskEnvironment().FastForwardBy(PdfCaret::kDefaultBlinkInterval -
@@ -436,10 +448,7 @@
   SetUpPagesWithCharCounts({1});
   SetUpChar(kTestChar0, 'a', {kTestChar0ScreenRect});
   InitializeCaretAtChar(kTestChar0);
-
-  EXPECT_EQ(gfx::Rect(), client().invalidated_rect());
-
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   EXPECT_EQ(kTestChar0Caret, client().invalidated_rect());
 
@@ -486,7 +495,7 @@
   SetUpPagesWithCharCounts({1});
   SetUpChar(kTestChar0, 'a', {kTestChar0ScreenRect});
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   EXPECT_EQ(kTestChar0Caret, client().invalidated_rect());
 
@@ -507,7 +516,7 @@
   // Set up second char two pixels to the right of the first char.
   SetUpChar({0, 1}, 'b', {gfx::Rect(24, 10, 12, 14)});
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   caret().SetCharAndDraw(kTestChar0);
   TestDrawCaret(kTestChar0Caret);
@@ -521,11 +530,11 @@
 
   // Setting the position should still work, even when not visible. The effects
   // will only appear when the caret is set to visible again.
-  caret().SetVisibility(false);
+  caret().SetEnabled(false);
   caret().SetCharAndDraw(kTestChar0);
   EXPECT_EQ(kSecondCharEndCaret, client().invalidated_rect());
 
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
   TestDrawCaret(kTestChar0Caret);
 }
 
@@ -533,7 +542,7 @@
   SetUpPagesWithCharCounts({4});
   SetUpChar(kTestChar0, 'a', {kTestChar0ScreenRect});
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   caret().SetCharAndDraw(kTestChar0);
   TestDrawCaret(kTestChar0Caret);
@@ -559,7 +568,7 @@
 TEST_F(PdfCaretTest, SetCharAndDrawMultiPage) {
   SetUpMultiPageTest();
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   caret().SetCharAndDraw(kTestChar0);
   TestDrawCaret(kTestChar0Caret);
@@ -607,7 +616,7 @@
   InitializeCaretAtChar(kTestChar0);
 
   // Relevant key events still handled even when caret is not visible.
-  caret().SetVisibility(false);
+  caret().SetEnabled(false);
 
   EXPECT_FALSE(
       caret().OnKeyDown(GenerateKeyboardEvent(ui::KeyboardCode::VKEY_0)));
@@ -620,7 +629,7 @@
   EXPECT_TRUE(
       caret().OnKeyDown(GenerateKeyboardEvent(ui::KeyboardCode::VKEY_DOWN)));
 
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   EXPECT_FALSE(
       caret().OnKeyDown(GenerateKeyboardEvent(ui::KeyboardCode::VKEY_0)));
@@ -640,7 +649,7 @@
 
   // Start at left of char 0.
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Left of char 1.
   SetUpChar({0, 1}, 'b', {kTestChar1ScreenRect});
@@ -682,7 +691,7 @@
 
   // Start at left of page 1, char 0.
   InitializeCaretAtChar({1, 0});
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Right of page 0, char 0.
   EXPECT_TRUE(
@@ -735,7 +744,7 @@
 
   // Start at left of page 0, char 0.
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Right of page 0, char 0.
   EXPECT_TRUE(
@@ -760,7 +769,7 @@
 
   // Start at left of page 0, char 0.
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Left of page 0, char 1.
   EXPECT_TRUE(
@@ -794,7 +803,7 @@
 
   // Start at left of page 0, char 1.
   InitializeCaretAtChar({0, 1});
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Left of page 0, char 3 '\n', skipping one newline.
   constexpr gfx::Rect kTestChar3Caret{10, 26, 1, 14};
@@ -827,7 +836,7 @@
 
   // Start at left of page 0, char 0.
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Right of page 0, char 1.
   EXPECT_TRUE(
@@ -855,7 +864,7 @@
 
   // Start at left of char 0.
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Left of char 3 'b'.
   EXPECT_TRUE(
@@ -873,7 +882,7 @@
   SetUpChar(kTestChar0, '\0', {kDefaultCaret});
 
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   EXPECT_TRUE(
       caret().OnKeyDown(GenerateKeyboardEvent(ui::KeyboardCode::VKEY_DOWN)));
@@ -892,7 +901,7 @@
 
   // Start at right of char 0.
   InitializeCaretAtChar({0, 1});
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Left of char 0.
   EXPECT_TRUE(
@@ -932,7 +941,7 @@
 
   // Start at left of char 1 'b'.
   InitializeCaretAtChar({0, 1});
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Left of char 5 'd'.
   constexpr gfx::Rect kTestChar5Caret{21, 26, 1, 12};
@@ -969,7 +978,7 @@
 
   // Start at right of char 1 '\r'.
   InitializeCaretAtChar(kStartNewline);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
   TestDrawCaret(kTestChar1EndCaret);
 
   // Right of char 5 'd'.
@@ -992,7 +1001,7 @@
 
   // Start at right of page 0, char 0 'a'.
   InitializeCaretAtChar({0, 1});
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Left of page 1, char 1 'c', which is closer than the right.
   EXPECT_TRUE(
@@ -1038,7 +1047,7 @@
   // Start at left of char 2 'c'.
   constexpr gfx::Rect kTestChar2Caret{34, 10, 1, 14};
   InitializeCaretAtChar({0, 2});
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
   TestDrawCaret(kTestChar2Caret);
 
   // Move down to char with closest screen rect. Right of char 5 'd'.
@@ -1064,7 +1073,7 @@
 
   // Start at right of char 5 'd'.
   InitializeCaretAtChar({0, 6});
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
   TestDrawCaret(gfx::Rect(46, 22, 1, 14));
 
   // Right of char 0 'a'.
@@ -1114,7 +1123,7 @@
 
   // Start at left of char 0.
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Move right. Select char 1.
   EXPECT_CALL(client(), IsSelecting()).WillOnce(Return(false));
@@ -1136,7 +1145,7 @@
   // Start at right of char 2.
   constexpr PageCharacterIndex kTestChar2End{0, 3};
   InitializeCaretAtChar(kTestChar2End);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Move left. Select char 2.
   EXPECT_CALL(client(), IsSelecting()).WillOnce(Return(false));
@@ -1158,7 +1167,7 @@
   // Start at left of char 1 'b'.
   constexpr PageCharacterIndex kTestChar1{0, 1};
   InitializeCaretAtChar(kTestChar1);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Move down. Select chars 1, 2, 3, 4.
   EXPECT_CALL(client(), IsSelecting()).WillOnce(Return(false));
@@ -1194,7 +1203,7 @@
   // Start at left of char 9 'f'.
   constexpr PageCharacterIndex kTestChar9{0, 9};
   InitializeCaretAtChar(kTestChar9);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Move up. Select chars 8, 7, 6, 5.
   EXPECT_CALL(client(), IsSelecting()).WillOnce(Return(false));
@@ -1229,7 +1238,7 @@
   SetUpChar({1, 0}, '\0', {gfx::Rect(10, 50, 1, 12)});
 
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Moving from a no-text page to another no-text page should not start a
   // selection.
@@ -1251,7 +1260,7 @@
   SetUpChar({2, 0}, '\0', {gfx::Rect(10, 100, 1, 12)});
 
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Select page 0, char 0.
   EXPECT_CALL(client(), IsSelecting()).WillOnce(Return(false));
@@ -1278,7 +1287,7 @@
   SetUpChar(kTestChar0, '\0', {kDefaultCaret});
 
   InitializeCaretAtChar(kTestChar0);
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   EXPECT_CALL(client(), StartSelection(_)).Times(0);
   EXPECT_CALL(client(), ExtendAndInvalidateSelectionByChar(_)).Times(0);
@@ -1298,7 +1307,7 @@
 
   // Start on the no-text page.
   InitializeCaretAtChar({2, 0});
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // `StartSelection()` should be called on the nearest caret position in the
   // direction of movement. In this case, it would be right of page 1, char 1.
@@ -1315,7 +1324,7 @@
 
   // Start at right of page 0, char 0.
   InitializeCaretAtChar({0, 1});
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Move down. Select page 1, char 0 'b'.
   EXPECT_CALL(client(), IsSelecting()).WillOnce(Return(false));
@@ -1352,7 +1361,7 @@
 
   // Start at right of page 3, char 0 'd'.
   InitializeCaretAtChar({3, 1});
-  caret().SetVisibility(true);
+  caret().SetEnabled(true);
 
   // Move up. Select page 3, char 0. Caret should be on no-text page.
   EXPECT_CALL(client(), IsSelecting()).WillOnce(Return(false));
diff --git a/pdf/pdfium/pdfium_draw_selection_test_base.cc b/pdf/pdfium/pdfium_draw_selection_test_base.cc
index 24653fc..e82ab6c 100644
--- a/pdf/pdfium/pdfium_draw_selection_test_base.cc
+++ b/pdf/pdfium/pdfium_draw_selection_test_base.cc
@@ -53,6 +53,16 @@
                      /*draw_caret=*/false);
 }
 
+void PDFiumDrawSelectionTestBase::DrawCaretAndCompare(
+    PDFiumEngine& engine,
+    int page_index,
+    std::string_view expected_png_filename) {
+  DrawAndCompareImpl(engine, page_index, FILE_PATH_LITERAL("caret"),
+                     expected_png_filename,
+                     /*use_platform_suffix=*/false,
+                     /*draw_caret=*/true);
+}
+
 void PDFiumDrawSelectionTestBase::DrawCaretAndCompareWithPlatformExpectations(
     PDFiumEngine& engine,
     int page_index,
diff --git a/pdf/pdfium/pdfium_draw_selection_test_base.h b/pdf/pdfium/pdfium_draw_selection_test_base.h
index 347b83b..2395a7d 100644
--- a/pdf/pdfium/pdfium_draw_selection_test_base.h
+++ b/pdf/pdfium/pdfium_draw_selection_test_base.h
@@ -52,6 +52,12 @@
   // caret is visible. The plugin size of `engine` must have a non-empty
   // intersect with the page contents rect of page `page_index`, i.e. the page
   // must be visible, otherwise the test will fail.
+  void DrawCaretAndCompare(PDFiumEngine& engine,
+                           int page_index,
+                           std::string_view expected_png_filename);
+
+  // Same as `DrawCaretAndCompare()`, but has different expected PNGs for
+  // specific platforms.
   void DrawCaretAndCompareWithPlatformExpectations(
       PDFiumEngine& engine,
       int page_index,
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index bdd31949..82739aa 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -967,6 +967,9 @@
 void PDFiumEngine::ClearTextSelection() {
   SelectionChangeInvalidator selection_invalidator(this);
   selection_.clear();
+  if (caret_) {
+    caret_->SetVisible(true);
+  }
 }
 
 void PDFiumEngine::ExtendAndInvalidateSelectionByChar(
@@ -976,6 +979,9 @@
 
   SelectionChangeInvalidator selection_invalidator(this);
   ExtendSelectionByChar(index);
+  if (caret_) {
+    caret_->SetVisible(!IsSelecting());
+  }
 }
 
 uint32_t PDFiumEngine::GetCharCount(uint32_t page_index) const {
@@ -1193,7 +1199,7 @@
   }
 
   // TODO(crbug.com/427778119): Set caret blink interval.
-  caret_->SetVisibility(enabled);
+  caret_->SetEnabled(enabled);
 }
 
 void PDFiumEngine::ContinueFind(bool case_sensitive) {
@@ -1343,6 +1349,7 @@
       if (last_focused_annot) {
         FPDF_BOOL ret = FORM_SetFocusedAnnot(form(), last_focused_annot.get());
         DCHECK(ret);
+        return;
       }
     }
   } else {
@@ -1362,6 +1369,12 @@
       FPDFPage_CloseAnnot(last_focused_annot);
     }
     KillFormFocus();
+    if (has_focus) {
+      return;
+    }
+  }
+  if (caret_) {
+    caret_->SetVisible(has_focus && !IsSelecting());
   }
 }
 
@@ -1565,6 +1578,7 @@
     if (caret_) {
       caret_->SetCharAndDraw(
           PageCharacterIndex(point_data.page_index, char_index));
+      caret_->SetVisible(true);
     }
   } else if (click_count >= 2) {
     OnMultipleClick(click_count, point_data.page_index, point_data.char_index);
@@ -1944,10 +1958,12 @@
   const uint32_t char_index = GetCharIndexBasedOnPointData(point_data);
   PageCharacterIndex index{static_cast<uint32_t>(point_data.page_index),
                            char_index};
+  const bool extended = ExtendSelectionByChar(index);
   if (caret_) {
     caret_->SetChar(index);
+    caret_->SetVisible(!IsSelecting());
   }
-  return ExtendSelectionByChar(index);
+  return extended;
 }
 
 bool PDFiumEngine::ExtendSelectionByChar(const PageCharacterIndex& index) {
@@ -2661,6 +2677,10 @@
       selection_.push_back(PDFiumRange::AllTextOnPage(page.get()));
     }
   }
+
+  if (caret_ && IsSelecting()) {
+    caret_->SetVisible(false);
+  }
 }
 
 const std::vector<DocumentAttachmentInfo>&
@@ -4256,6 +4276,11 @@
   if (focus_field_type_ != FocusFieldType::kText) {
     editable_form_text_area_ = false;
   }
+
+  if (caret_) {
+    caret_->SetVisible(focus_field_type_ == FocusFieldType::kNoFocus &&
+                       !IsSelecting());
+  }
 }
 
 void PDFiumEngine::SetMouseLeftButtonDown(bool is_mouse_left_button_down) {
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h
index 939bcb8..126be81 100644
--- a/pdf/pdfium/pdfium_engine.h
+++ b/pdf/pdfium/pdfium_engine.h
@@ -1330,7 +1330,9 @@
 
   PDFiumPrint print_;
 
-  // The text caret on the PDF, excluding AcroForms.
+  // The text caret on the PDF, excluding AcroForms. Once initialized, it will
+  // not be destroyed until the destructor is called. The caret needs to store
+  // state, such as its position, blink interval, etc.
   std::unique_ptr<PdfCaret> caret_;
 
   // The list of text fragments to highlight on the PDF.
diff --git a/pdf/pdfium/pdfium_engine_unittest.cc b/pdf/pdfium/pdfium_engine_unittest.cc
index 012c12d..1637fb5 100644
--- a/pdf/pdfium/pdfium_engine_unittest.cc
+++ b/pdf/pdfium/pdfium_engine_unittest.cc
@@ -33,6 +33,7 @@
 #include "pdf/page_character_index.h"
 #include "pdf/pdf_features.h"
 #include "pdf/pdfium/pdfium_draw_selection_test_base.h"
+#include "pdf/pdfium/pdfium_engine_client.h"
 #include "pdf/pdfium/pdfium_page.h"
 #include "pdf/pdfium/pdfium_test_base.h"
 #include "pdf/test/mouse_event_builder.h"
@@ -2973,11 +2974,15 @@
 
 class PDFiumEngineCaretTest : public PDFiumDrawSelectionTestBase {
  public:
+  static constexpr gfx::Size kAnnotationFormFieldsVisiblePageSize{816, 1056};
+  static constexpr gfx::Size kHelloWorldExpectedVisiblePageSize{266, 266};
   PDFiumEngineCaretTest() = default;
   PDFiumEngineCaretTest(const PDFiumEngineCaretTest&) = delete;
   PDFiumEngineCaretTest& operator=(const PDFiumEngineCaretTest&) = delete;
   ~PDFiumEngineCaretTest() override = default;
 
+  MockTestClient& client() { return client_; }
+
   void SetUp() override {
     PDFiumDrawSelectionTestBase::SetUp();
 
@@ -2999,14 +3004,24 @@
       // Plugin size chosen so all pages of the document are visible.
       engine_->PluginSizeUpdated({1024, 4096});
       engine_->SetCaretBrowsingEnabled(true);
+      engine_->UpdateFocus(true);
     }
     return engine_.get();
   }
 
+  bool HandleKeyDownEvent(ui::KeyboardCode key) {
+    blink::WebKeyboardEvent event(
+        blink::WebInputEvent::Type::kKeyDown,
+        blink::WebInputEvent::kNoModifiers,
+        blink::WebInputEvent::GetStaticTimeStampForTests());
+    event.windows_key_code = key;
+    return engine_->HandleInputEvent(event);
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
   std::unique_ptr<PDFiumEngine> engine_;
-  TestClient client_;
+  NiceMock<MockTestClient> client_;
 };
 
 TEST_P(PDFiumEngineCaretTest, SetCaretBrowsingEnabled) {
@@ -3018,7 +3033,6 @@
 
   engine->SetCaretBrowsingEnabled(false);
 
-  constexpr gfx::Size kHelloWorldExpectedVisiblePageSize{266, 266};
   DrawCaretAndExpectBlank(*engine, /*page_index=*/0,
                           kHelloWorldExpectedVisiblePageSize);
 
@@ -3028,6 +3042,24 @@
                                               "hello_world_caret.png");
 }
 
+TEST_P(PDFiumEngineCaretTest, UpdateFocus) {
+  PDFiumEngine* engine = CreateEngine(FILE_PATH_LITERAL("hello_world2.pdf"));
+  ASSERT_TRUE(engine);
+
+  DrawCaretAndCompareWithPlatformExpectations(*engine, /*page_index=*/0,
+                                              "hello_world_caret.png");
+
+  engine->UpdateFocus(false);
+
+  DrawCaretAndExpectBlank(*engine, /*page_index=*/0,
+                          kHelloWorldExpectedVisiblePageSize);
+
+  engine->UpdateFocus(true);
+
+  DrawCaretAndCompareWithPlatformExpectations(*engine, /*page_index=*/0,
+                                              "hello_world_caret.png");
+}
+
 TEST_P(PDFiumEngineCaretTest, DrawOnGeometryChange) {
   PDFiumEngine* engine = CreateEngine(FILE_PATH_LITERAL("hello_world2.pdf"));
   ASSERT_TRUE(engine);
@@ -3110,11 +3142,7 @@
   DrawCaretAndCompareWithPlatformExpectations(
       *engine, /*page_index=*/0, "hello_world_caret_text_selection.png");
 
-  blink::WebKeyboardEvent key_down_event(
-      blink::WebInputEvent::Type::kKeyDown, blink::WebInputEvent::kNoModifiers,
-      blink::WebInputEvent::GetStaticTimeStampForTests());
-  key_down_event.windows_key_code = ui::KeyboardCode::VKEY_RIGHT;
-  EXPECT_TRUE(engine->HandleInputEvent(key_down_event));
+  EXPECT_TRUE(HandleKeyDownEvent(ui::KeyboardCode::VKEY_RIGHT));
 
   // TODO(crbug.com/446944878): Caret should appear at the end of the text
   // selection.
@@ -3122,6 +3150,92 @@
       *engine, /*page_index=*/0, "hello_world_caret_text_selection_end.png");
 }
 
+TEST_P(PDFiumEngineCaretTest, TextSelectAndBack) {
+  PDFiumEngine* engine = CreateEngine(FILE_PATH_LITERAL("hello_world2.pdf"));
+  ASSERT_TRUE(engine);
+
+  engine->OnTextOrLinkAreaClick(kHelloWorldStartPosition, /*click_count=*/1);
+
+  DrawCaretAndCompareWithPlatformExpectations(*engine, /*page_index=*/0,
+                                              "hello_world_caret_start.png");
+
+  EXPECT_TRUE(engine->ExtendSelectionByPoint(kHelloWorldEndPosition));
+  EXPECT_TRUE(engine->ExtendSelectionByPoint(kHelloWorldStartPosition));
+
+  DrawCaretAndCompareWithPlatformExpectations(*engine, /*page_index=*/0,
+                                              "hello_world_caret_start.png");
+}
+
+TEST_P(PDFiumEngineCaretTest, SelectAll) {
+  PDFiumEngine* engine = CreateEngine(FILE_PATH_LITERAL("hello_world2.pdf"));
+  ASSERT_TRUE(engine);
+
+  DrawCaretAndCompareWithPlatformExpectations(*engine, /*page_index=*/0,
+                                              "hello_world_caret.png");
+
+  engine->SelectAll();
+
+  // Caret should not be visible.
+  DrawCaretAndCompareWithPlatformExpectations(
+      *engine, /*page_index=*/0, "hello_world_caret_text_selection_all.png");
+  DrawCaretAndCompareWithPlatformExpectations(
+      *engine, /*page_index=*/1, "hello_world_caret_text_selection_all.png");
+}
+
+TEST_P(PDFiumEngineCaretTest, FormFocus) {
+  PDFiumEngine* engine =
+      CreateEngine(FILE_PATH_LITERAL("annotation_form_fields.pdf"));
+  ASSERT_TRUE(engine);
+
+  // Focus onto a form field page element.
+  EXPECT_CALL(client(),
+              FormFieldFocusChange(PDFiumEngineClient::FocusFieldType::kText));
+  EXPECT_TRUE(HandleKeyDownEvent(ui::KeyboardCode::VKEY_TAB));
+  EXPECT_TRUE(HandleKeyDownEvent(ui::KeyboardCode::VKEY_TAB));
+
+  // Caret should not be visible.
+  DrawCaretAndExpectBlank(*engine, /*page_index=*/0,
+                          kAnnotationFormFieldsVisiblePageSize);
+
+  // Click outside the form field to kill focus.
+  EXPECT_CALL(client(), FormFieldFocusChange(
+                            PDFiumEngineClient::FocusFieldType::kNoFocus));
+  EXPECT_TRUE(engine->HandleInputEvent(
+      CreateLeftClickWebMouseEventAtPosition(gfx::PointF(1.0f, 1.0f))));
+
+  // Caret should be visible.
+  DrawCaretAndCompare(*engine, /*page_index=*/0,
+                      "annotation_form_fields_caret.png");
+}
+
+TEST_P(PDFiumEngineCaretTest, FormFieldLoseFocusGainFocus) {
+  PDFiumEngine* engine =
+      CreateEngine(FILE_PATH_LITERAL("annotation_form_fields.pdf"));
+  ASSERT_TRUE(engine);
+
+  // Focus onto a form field page element.
+  EXPECT_CALL(client(),
+              FormFieldFocusChange(PDFiumEngineClient::FocusFieldType::kText));
+  EXPECT_TRUE(HandleKeyDownEvent(ui::KeyboardCode::VKEY_TAB));
+  EXPECT_TRUE(HandleKeyDownEvent(ui::KeyboardCode::VKEY_TAB));
+
+  // Caret should not be visible.
+  DrawCaretAndExpectBlank(*engine, /*page_index=*/0,
+                          kAnnotationFormFieldsVisiblePageSize);
+
+  EXPECT_CALL(client(), FormFieldFocusChange(
+                            PDFiumEngineClient::FocusFieldType::kNoFocus));
+  engine->UpdateFocus(false);
+
+  EXPECT_CALL(client(),
+              FormFieldFocusChange(PDFiumEngineClient::FocusFieldType::kText));
+  engine->UpdateFocus(true);
+
+  // Form still has focus, so caret should not be visible.
+  DrawCaretAndExpectBlank(*engine, /*page_index=*/0,
+                          kAnnotationFormFieldsVisiblePageSize);
+}
+
 INSTANTIATE_TEST_SUITE_P(All, PDFiumEngineCaretTest, testing::Bool());
 
 #endif  // BUILDFLAG(ENABLE_PDF_INK2)
diff --git a/pdf/test/data/caret/annotation_form_fields_caret.png b/pdf/test/data/caret/annotation_form_fields_caret.png
new file mode 100644
index 0000000..ab5f5ff
--- /dev/null
+++ b/pdf/test/data/caret/annotation_form_fields_caret.png
Binary files differ
diff --git a/pdf/test/data/caret/hello_world_caret_text_selection_all.png b/pdf/test/data/caret/hello_world_caret_text_selection_all.png
new file mode 100644
index 0000000..e54003f
--- /dev/null
+++ b/pdf/test/data/caret/hello_world_caret_text_selection_all.png
Binary files differ
diff --git a/pdf/test/data/caret/hello_world_caret_text_selection_all_mac.png b/pdf/test/data/caret/hello_world_caret_text_selection_all_mac.png
new file mode 100644
index 0000000..bc59f465
--- /dev/null
+++ b/pdf/test/data/caret/hello_world_caret_text_selection_all_mac.png
Binary files differ
diff --git a/pdf/test/data/caret/hello_world_caret_text_selection_all_win.png b/pdf/test/data/caret/hello_world_caret_text_selection_all_win.png
new file mode 100644
index 0000000..9fd9714
--- /dev/null
+++ b/pdf/test/data/caret/hello_world_caret_text_selection_all_win.png
Binary files differ
diff --git a/printing/backend/mojom/print_backend_mojom_traits.cc b/printing/backend/mojom/print_backend_mojom_traits.cc
index e8ea72c..d8f13a22 100644
--- a/printing/backend/mojom/print_backend_mojom_traits.cc
+++ b/printing/backend/mojom/print_backend_mojom_traits.cc
@@ -12,25 +12,15 @@
 #include "ui/gfx/geometry/mojom/geometry.mojom-shared.h"
 #include "ui/gfx/geometry/mojom/geometry_mojom_traits.h"
 
-// Implementations of std::less<> here are for purposes of detecting duplicate
+// Implementations of Less here are for purposes of detecting duplicate
 // entries in arrays.  They do not require strict checks of all fields, but
 // instead focus on identifying attributes that would be used to clearly
 // distinguish properties to a user.  E.g., if two entries have the same
 // displayable name but different corresponding values, consider that to be a
 // duplicate for these purposes.
-namespace std {
+namespace {
 
-template <>
-struct less<::gfx::Size> {
-  bool operator()(const ::gfx::Size& lhs, const ::gfx::Size& rhs) const {
-    if (lhs.width() < rhs.width())
-      return true;
-    return lhs.height() < rhs.height();
-  }
-};
-
-template <>
-struct less<::printing::PrinterSemanticCapsAndDefaults::Paper> {
+struct LessPaper {
   bool operator()(
       const ::printing::PrinterSemanticCapsAndDefaults::Paper& lhs,
       const ::printing::PrinterSemanticCapsAndDefaults::Paper& rhs) const {
@@ -42,8 +32,8 @@
 };
 
 #if BUILDFLAG(IS_CHROMEOS)
-template <>
-struct less<::printing::AdvancedCapability> {
+
+struct LessAdvancedCapability {
   bool operator()(const ::printing::AdvancedCapability& lhs,
                   const ::printing::AdvancedCapability& rhs) const {
     if (lhs.name < rhs.name)
@@ -51,9 +41,10 @@
     return lhs.display_name < rhs.display_name;
   }
 };
+
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
-}  // namespace std
+}  // namespace
 
 namespace mojo {
 
@@ -77,9 +68,9 @@
 
 namespace {
 
-template <class Key>
-bool HasDuplicateItems(const std::vector<Key>& items) {
-  std::set<Key> items_encountered;
+template <class Key, class Less = std::less<Key>>
+bool HasDuplicateItems(const std::vector<Key>& items, Less = {}) {
+  std::set<Key, Less> items_encountered;
   for (const Key& item : items) {
     bool inserted = items_encountered.insert(item).second;
     if (!inserted) {
@@ -327,13 +318,13 @@
     return false;
   }
 
-  if (HasDuplicateItems(out->user_defined_papers)) {
+  if (HasDuplicateItems(out->user_defined_papers, LessPaper{})) {
     DLOG(ERROR) << "Duplicate user_defined_papers detected.";
     return false;
   }
 
 #if BUILDFLAG(IS_CHROMEOS)
-  if (HasDuplicateItems(out->advanced_capabilities)) {
+  if (HasDuplicateItems(out->advanced_capabilities, LessAdvancedCapability{})) {
     DLOG(ERROR) << "Duplicate advanced_capabilities detected.";
     return false;
   }
diff --git a/printing/backend/mojom/print_backend_mojom_traits_unittest.cc b/printing/backend/mojom/print_backend_mojom_traits_unittest.cc
index 54df51f..101f2ea 100644
--- a/printing/backend/mojom/print_backend_mojom_traits_unittest.cc
+++ b/printing/backend/mojom/print_backend_mojom_traits_unittest.cc
@@ -26,7 +26,7 @@
 using ::testing::UnorderedElementsAreArray;
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
-TEST(PrintBackendMojomTraitsTest, TestSerializeAndDeserializePrinterBasicInfo) {
+TEST(PrintBackendMojomTraitsTest, PrinterBasicInfo) {
   static const PrinterBasicInfo kPrinterBasicInfo1(
       /*printer_name=*/"test printer name 1",
       /*display_name=*/"test display name 1",
@@ -54,8 +54,7 @@
   }
 }
 
-TEST(PrintBackendMojomTraitsTest,
-     TestSerializeAndDeserializePrinterBasicInfoEmptyNames) {
+TEST(PrintBackendMojomTraitsTest, PrinterBasicInfoEmptyNames) {
   static const PrinterBasicInfo kPrinterBasicInfoEmptyPrinterName(
       /*printer_name=*/"",
       /*display_name=*/"test display name",
@@ -77,7 +76,7 @@
   }
 }
 
-TEST(PrintBackendMojomTraitsTest, TestSerializeAndDeserializePaper) {
+TEST(PrintBackendMojomTraitsTest, Paper) {
   PrinterSemanticCapsAndDefaults::Papers test_papers = kPapers;
   test_papers.push_back(kPaperCustom);
 
@@ -90,7 +89,7 @@
   }
 }
 
-TEST(PrintBackendMojomTraitsTest, TestPaperCtors) {
+TEST(PrintBackendMojomTraitsTest, PaperCtors) {
   // All constructors should be able to generate valid papers.
   constexpr gfx::Size kNonEmptySize(100, 200);
   constexpr gfx::Rect kNonEmptyPrintableArea(kNonEmptySize);
@@ -138,7 +137,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 }
 
-TEST(PrintBackendMojomTraitsTest, TestPaperEmpty) {
+TEST(PrintBackendMojomTraitsTest, PaperEmpty) {
   // Empty Papers should be valid.
   PrinterSemanticCapsAndDefaults::Paper input;
   PrinterSemanticCapsAndDefaults::Paper output;
@@ -147,7 +146,7 @@
   EXPECT_EQ(input, output);
 }
 
-TEST(PrintBackendMojomTraitsTest, TestPaperInvalidCustomSize) {
+TEST(PrintBackendMojomTraitsTest, PaperInvalidCustomSize) {
   // The min height is larger than the max height, so it should be invalid.
   PrinterSemanticCapsAndDefaults::Paper input{
       /*display_name=*/"display_name",
@@ -162,7 +161,7 @@
       mojo::test::SerializeAndDeserialize<mojom::Paper>(input, output));
 }
 
-TEST(PrintBackendMojomTraitsTest, TestPaperEmptyPrintableArea) {
+TEST(PrintBackendMojomTraitsTest, PaperEmptyPrintableArea) {
   // The printable area is empty, but the other fields are not, so it should be
   // invalid.
   PrinterSemanticCapsAndDefaults::Paper input{
@@ -175,7 +174,7 @@
       mojo::test::SerializeAndDeserialize<mojom::Paper>(input, output));
 }
 
-TEST(PrintBackendMojomTraitsTest, TestPaperPrintableAreaLargerThanSize) {
+TEST(PrintBackendMojomTraitsTest, PaperPrintableAreaLargerThanSize) {
   // The printable area is larger than the size, so it should be invalid.
   PrinterSemanticCapsAndDefaults::Paper input{
       /*display_name=*/"display_name", /*vendor_id=*/"vendor_id",
@@ -187,7 +186,7 @@
       mojo::test::SerializeAndDeserialize<mojom::Paper>(input, output));
 }
 
-TEST(PrintBackendMojomTraitsTest, TestPaperPrintableAreaLargerThanCustomSize) {
+TEST(PrintBackendMojomTraitsTest, PaperPrintableAreaLargerThanCustomSize) {
   // The printable area is larger than the custom size, so it should be invalid.
   PrinterSemanticCapsAndDefaults::Paper input{
       /*display_name=*/"display_name",
@@ -201,7 +200,7 @@
       mojo::test::SerializeAndDeserialize<mojom::Paper>(input, output));
 }
 
-TEST(PrintBackendMojomTraitsTest, TestPaperPrintableAreaOutOfBounds) {
+TEST(PrintBackendMojomTraitsTest, PaperPrintableAreaOutOfBounds) {
   // The printable area is out of bounds of the size, so it should be invalid.
   PrinterSemanticCapsAndDefaults::Paper input{
       /*display_name=*/"display_name", /*vendor_id=*/"vendor_id",
@@ -213,7 +212,7 @@
       mojo::test::SerializeAndDeserialize<mojom::Paper>(input, output));
 }
 
-TEST(PrintBackendMojomTraitsTest, TestPaperNegativePrintableArea) {
+TEST(PrintBackendMojomTraitsTest, PaperNegativePrintableArea) {
   // The printable area has negative x and y values, so it should be invalid.
   PrinterSemanticCapsAndDefaults::Paper input{
       /*display_name=*/"display_name", /*vendor_id=*/"vendor_id",
@@ -226,7 +225,7 @@
 }
 
 #if BUILDFLAG(IS_CHROMEOS)
-TEST(PrintBackendMojomTraitsTest, TestValidMargins) {
+TEST(PrintBackendMojomTraitsTest, ValidMargins) {
   PrinterSemanticCapsAndDefaults::Paper input{
       /*display_name=*/"display_name",
       /*vendor_id=*/"vendor_id",
@@ -240,7 +239,7 @@
   EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Paper>(input, output));
 }
 
-TEST(PrintBackendMojomTraitsTest, TestInvalidMargins) {
+TEST(PrintBackendMojomTraitsTest, InvalidMargins) {
   // The printable area is valid, but the margins are invalid, so it should be
   // invalid. The margins are invalid because content width and the top margins
   // are negative.
@@ -258,8 +257,7 @@
       mojo::test::SerializeAndDeserialize<mojom::Paper>(input, output));
 }
 
-TEST(PrintBackendMojomTraitsTest,
-     TestSerializeAndDeserializeAdvancedCapability) {
+TEST(PrintBackendMojomTraitsTest, AdvancedCapability) {
   for (const auto& advanced_capability : kAdvancedCapabilities) {
     AdvancedCapability input = advanced_capability;
     AdvancedCapability output;
@@ -268,9 +266,9 @@
     EXPECT_EQ(advanced_capability, output);
   }
 }
-TEST(
-    PrintBackendMojomTraitsTest,
-    TestSerializeAndDeserializePrinterSemanticCapsAndDefaultsPrintScalingTypes) {
+
+TEST(PrintBackendMojomTraitsTest,
+     PrinterSemanticCapsAndDefaultsPrintScalingTypes) {
   // Normal scenario: valid types and default value
   {
     PrinterSemanticCapsAndDefaults input =
@@ -314,9 +312,8 @@
   }
 }
 
-TEST(
-    PrintBackendMojomTraitsTest,
-    TestSerializeAndDeserializePrinterSemanticCapsAndDefaultsPrintScalingTypesDuplicate) {
+TEST(PrintBackendMojomTraitsTest,
+     PrinterSemanticCapsAndDefaultsPrintScalingTypesDuplicate) {
   // Duplicates in print_scaling_types (should be invalid)
   PrinterSemanticCapsAndDefaults input =
       GenerateSamplePrinterSemanticCapsAndDefaults(
@@ -333,8 +330,7 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
-TEST(PrintBackendMojomTraitsTest,
-     TestSerializeAndDeserializePrinterSemanticCapsAndDefaults) {
+TEST(PrintBackendMojomTraitsTest, PrinterSemanticCapsAndDefaults) {
   OptionalSampleCapabilities caps;
 #if BUILDFLAG(IS_CHROMEOS)
   caps = SampleWithScaleAndPinAndAdvancedCapabilities();
@@ -371,8 +367,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 }
 
-TEST(PrintBackendMojomTraitsTest,
-     TestSerializeAndDeserializePrinterSemanticCapsAndDefaultsCopiesMax) {
+TEST(PrintBackendMojomTraitsTest, PrinterSemanticCapsAndDefaultsCopiesMax) {
   PrinterSemanticCapsAndDefaults input =
       GenerateSamplePrinterSemanticCapsAndDefaults({});
   PrinterSemanticCapsAndDefaults output;
@@ -384,9 +379,8 @@
                mojom::PrinterSemanticCapsAndDefaults>(input, output));
 }
 
-TEST(
-    PrintBackendMojomTraitsTest,
-    TestSerializeAndDeserializePrinterSemanticCapsAndDefaultsAllowableEmptyArrays) {
+TEST(PrintBackendMojomTraitsTest,
+     PrinterSemanticCapsAndDefaultsAllowableEmptyArrays) {
   PrinterSemanticCapsAndDefaults input =
       GenerateSamplePrinterSemanticCapsAndDefaults({});
   PrinterSemanticCapsAndDefaults output;
@@ -418,8 +412,7 @@
 #endif
 }
 
-TEST(PrintBackendMojomTraitsTest,
-     TestSerializeAndDeserializePrinterSemanticCapsAndDefaultsEmptyPapers) {
+TEST(PrintBackendMojomTraitsTest, PrinterSemanticCapsAndDefaultsEmptyPapers) {
   PrinterSemanticCapsAndDefaults input =
       GenerateSamplePrinterSemanticCapsAndDefaults({});
   PrinterSemanticCapsAndDefaults output;
@@ -436,7 +429,7 @@
 }
 
 TEST(PrintBackendMojomTraitsTest,
-     TestSerializeAndDeserializePrinterSemanticCapsAndDefaultsEmptyMediaTypes) {
+     PrinterSemanticCapsAndDefaultsEmptyMediaTypes) {
   PrinterSemanticCapsAndDefaults input =
       GenerateSamplePrinterSemanticCapsAndDefaults({});
   PrinterSemanticCapsAndDefaults output;
@@ -451,9 +444,8 @@
   EXPECT_EQ(kEmptyMediaTypes, output.media_types);
 }
 
-TEST(
-    PrintBackendMojomTraitsTest,
-    TestSerializeAndDeserializePrinterSemanticCapsAndDefaultsNoDuplicatesInArrays) {
+TEST(PrintBackendMojomTraitsTest,
+     PrinterSemanticCapsAndDefaultsNoDuplicatesInArrays) {
   PrinterSemanticCapsAndDefaults input =
       GenerateSamplePrinterSemanticCapsAndDefaults({});
   PrinterSemanticCapsAndDefaults output;
@@ -486,9 +478,8 @@
 #endif
 }
 
-TEST(
-    PrintBackendMojomTraitsTest,
-    TestSerializeAndDeserializePrinterSemanticCapsAndDefaultsAllowedDuplicatesInArrays) {
+TEST(PrintBackendMojomTraitsTest,
+     PrinterSemanticCapsAndDefaultsAllowedDuplicatesInArrays) {
   PrinterSemanticCapsAndDefaults input =
       GenerateSamplePrinterSemanticCapsAndDefaults({});
   PrinterSemanticCapsAndDefaults output;
@@ -521,8 +512,7 @@
 }
 
 #if BUILDFLAG(IS_WIN)
-TEST(PrintBackendMojomTraitsTest,
-     TestSerializeAndDeserializePageOutputQualityAttribute) {
+TEST(PrintBackendMojomTraitsTest, PageOutputQualityAttribute) {
   PageOutputQualityAttribute input = kPageOutputQualityAttribute1;
   PageOutputQualityAttribute output;
   EXPECT_TRUE(
@@ -532,8 +522,7 @@
   EXPECT_EQ(kPageOutputQualityAttribute1.name, output.name);
 }
 
-TEST(PrintBackendMojomTraitsTest,
-     TestSerializeAndDeserializePageOutputQuality) {
+TEST(PrintBackendMojomTraitsTest, PageOutputQuality) {
   PageOutputQuality input = kPageOutputQuality;
   PageOutputQuality output;
   EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::PageOutputQuality>(
@@ -543,7 +532,7 @@
 }
 
 TEST(PrintBackendMojomTraitsTest,
-     TestSerializeAndDeserializePrinterSemanticCapsAndDefaultsXpsCapabilities) {
+     PrinterSemanticCapsAndDefaultsXpsCapabilities) {
   PrinterSemanticCapsAndDefaults input =
       GenerateSamplePrinterSemanticCapsAndDefaults(
           SampleWithPageOutputQuality());
@@ -557,9 +546,8 @@
             output.page_output_quality->default_quality);
 }
 
-TEST(
-    PrintBackendMojomTraitsTest,
-    TestSerializeAndDeserializePrinterSemanticCapsAndDefaultsAllowableEmptyArraysXpsCapabilities) {
+TEST(PrintBackendMojomTraitsTest,
+     PrinterSemanticCapsAndDefaultsAllowableEmptyArraysXpsCapabilities) {
   const PageOutputQualityAttributes kEmptyQualities;
   PageOutputQuality quality(kEmptyQualities, /*default_quality=*/std::nullopt);
   PrinterSemanticCapsAndDefaults input =
@@ -573,9 +561,8 @@
   EXPECT_EQ(kEmptyQualities, output.page_output_quality->qualities);
 }
 
-TEST(
-    PrintBackendMojomTraitsTest,
-    TestSerializeAndDeserializePrinterSemanticCapsAndDefaultsDefaultQualityInArraysXpsCapabilities) {
+TEST(PrintBackendMojomTraitsTest,
+     PrinterSemanticCapsAndDefaultsDefaultQualityInArraysXpsCapabilities) {
   PrinterSemanticCapsAndDefaults input =
       GenerateSamplePrinterSemanticCapsAndDefaults(
           SampleWithPageOutputQuality());
@@ -591,7 +578,7 @@
 
 TEST(
     PrintBackendMojomTraitsTest,
-    TestSerializeAndDeserializePrinterSemanticCapsAndDefaultsMissingDefaultQualityInArraysXpsCapabilities) {
+    PrinterSemanticCapsAndDefaultsMissingDefaultQualityInArraysXpsCapabilities) {
   PrinterSemanticCapsAndDefaults input =
       GenerateSamplePrinterSemanticCapsAndDefaults(
           SampleWithPageOutputQuality());
@@ -604,9 +591,8 @@
                mojom::PrinterSemanticCapsAndDefaults>(input, output));
 }
 
-TEST(
-    PrintBackendMojomTraitsTest,
-    TestSerializeAndDeserializePrinterSemanticCapsAndDefaultsNoDuplicatesInArraysXpsCapabilities) {
+TEST(PrintBackendMojomTraitsTest,
+     PrinterSemanticCapsAndDefaultsNoDuplicatesInArraysXpsCapabilities) {
   // `kPageOutputQualityAttributePrime` has same display_name and name with
   // `kPageOutputQualityAttribute1`, which is not allowed.
   const PageOutputQualityAttribute kPageOutputQualityAttributePrime(
diff --git a/remoting/host/desktop_process_unittest.cc b/remoting/host/desktop_process_unittest.cc
index 8e27d4b62..a3ee191 100644
--- a/remoting/host/desktop_process_unittest.cc
+++ b/remoting/host/desktop_process_unittest.cc
@@ -63,7 +63,6 @@
 
   ~MockDaemonListener() override = default;
 
-  bool OnMessageReceived(const IPC::Message& message) override;
   void OnAssociatedInterfaceRequest(
       const std::string& interface_name,
       mojo::ScopedInterfaceEndpointHandle handle) override;
@@ -93,19 +92,12 @@
 
   ~MockNetworkListener() override = default;
 
-  bool OnMessageReceived(const IPC::Message& message) override;
-
   MOCK_METHOD(void, OnChannelConnected, (std::int32_t), (override));
   MOCK_METHOD(void, OnChannelError, (), (override));
 
   MOCK_METHOD0(OnDesktopEnvironmentCreated, void());
 };
 
-bool MockDaemonListener::OnMessageReceived(const IPC::Message& message) {
-  ADD_FAILURE() << "Unexpected call to OnMessageReceived()";
-  return false;
-}
-
 void MockDaemonListener::OnAssociatedInterfaceRequest(
     const std::string& interface_name,
     mojo::ScopedInterfaceEndpointHandle handle) {
@@ -119,11 +111,6 @@
   desktop_session_request_handler_.reset();
 }
 
-bool MockNetworkListener::OnMessageReceived(const IPC::Message& message) {
-  ADD_FAILURE() << "Unexpected call to OnMessageReceived()";
-  return false;
-}
-
 }  // namespace
 
 class DesktopProcessTest : public testing::Test {
diff --git a/remoting/host/win/unprivileged_process_delegate.cc b/remoting/host/win/unprivileged_process_delegate.cc
index 189c298..c1beb76 100644
--- a/remoting/host/win/unprivileged_process_delegate.cc
+++ b/remoting/host/win/unprivileged_process_delegate.cc
@@ -383,12 +383,6 @@
   }
 }
 
-bool UnprivilegedProcessDelegate::OnMessageReceived(
-    const IPC::Message& message) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  NOTREACHED() << "Received unexpected IPC type: " << message.type();
-}
-
 void UnprivilegedProcessDelegate::OnChannelConnected(int32_t peer_pid) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/remoting/host/win/unprivileged_process_delegate.h b/remoting/host/win/unprivileged_process_delegate.h
index a9249d3a..3b4eb2c 100644
--- a/remoting/host/win/unprivileged_process_delegate.h
+++ b/remoting/host/win/unprivileged_process_delegate.h
@@ -28,7 +28,6 @@
 
 namespace IPC {
 class ChannelProxy;
-class Message;
 }  // namespace IPC
 
 namespace remoting {
@@ -58,7 +57,6 @@
 
  private:
   // IPC::Listener implementation.
-  bool OnMessageReceived(const IPC::Message& message) override;
   void OnChannelConnected(int32_t peer_pid) override;
   void OnChannelError() override;
   void OnAssociatedInterfaceRequest(
diff --git a/remoting/signaling/corp_messaging_client.cc b/remoting/signaling/corp_messaging_client.cc
index b9aa027c..b9fca95 100644
--- a/remoting/signaling/corp_messaging_client.cc
+++ b/remoting/signaling/corp_messaging_client.cc
@@ -121,7 +121,7 @@
     const std::string& payload,
     StatusCallback on_done) {
   internal::SendHostMessageRequestStruct request;
-  request.simple_message.destination_id = destination_id;
+  request.destination_id = destination_id;
   request.simple_message.message_id =
       base::Uuid::GenerateRandomV4().AsLowercaseString();
   request.simple_message.payload = payload;
diff --git a/remoting/test/BUILD.gn b/remoting/test/BUILD.gn
index cf3e39b..22320b4 100644
--- a/remoting/test/BUILD.gn
+++ b/remoting/test/BUILD.gn
@@ -210,6 +210,8 @@
       "corp_messaging_playground.cc",
       "corp_messaging_playground.h",
       "corp_messaging_playground_main.cc",
+      "ping_pong_helper.cc",
+      "ping_pong_helper.h",
     ]
     deps = [
       "//mojo/core/embedder",
diff --git a/remoting/test/corp_messaging_playground.cc b/remoting/test/corp_messaging_playground.cc
index 747726d1..218572d 100644
--- a/remoting/test/corp_messaging_playground.cc
+++ b/remoting/test/corp_messaging_playground.cc
@@ -12,15 +12,20 @@
 #include "base/functional/callback.h"
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/notreached.h"
 #include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
 #include "base/task/bind_post_task.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "net/ssl/client_cert_store.h"
 #include "remoting/base/certificate_helpers.h"
 #include "remoting/base/http_status.h"
+#include "remoting/base/internal_headers.h"
 #include "remoting/base/url_request_context_getter.h"
 #include "remoting/signaling/corp_messaging_client.h"
+#include "remoting/test/ping_pong_helper.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/transitional_url_loader_factory_owner.h"
 
@@ -54,6 +59,7 @@
             << std::endl;
   std::cout << "Press '3' to send a burst of 100 messages to the client."
             << std::endl;
+  std::cout << "Press '4' to start a ping-pong exchange." << std::endl;
   std::cout << "Press 'x' to quit." << std::endl << std::endl;
 
   while (true) {
@@ -120,9 +126,38 @@
 
 void CorpMessagingPlayground::OnSimpleMessageReceived(
     const internal::SimpleMessageStruct& message) {
-  LOG(INFO) << "SimpleMessage received from " << message.sender_id.username
-            << ": " << message.payload;
+  // `create_time` is not used because it is set on the client and may be out of
+  // sync with the time values set by the server.
+  auto routing_latency = message.deliver_time - message.receive_time;
+  LOG(INFO) << "SimpleMessage received: sender=" << message.sender_id.username
+            << ", routing_latency=" << routing_latency.InMilliseconds()
+            << "ms, payload=" << message.payload;
   last_sender_id_ = message.sender_id;
+
+  if (IsPongMessage(message.payload)) {
+    auto rtt = base::Time::Now() - last_ping_sent_time_;
+    ping_total_rtt_ += rtt;
+    LOG(INFO) << "Current RTT: " << rtt.InMilliseconds()
+              << "ms, Total RTT: " << ping_total_rtt_.InMilliseconds() << "ms";
+    // Now respond with a ping unless we've reached our max count.
+    std::optional<std::string> ping_payload =
+        OnPingPongMessageReceived(message.payload);
+    if (ping_payload.has_value()) {
+      last_ping_sent_time_ = base::Time::Now();
+      client_->SendMessage(last_sender_id_, *ping_payload, base::DoNothing());
+    } else {
+      LOG(INFO) << "Ping-pong exchange finished. Total RTT: "
+                << ping_total_rtt_.InMilliseconds() << "ms";
+    }
+  } else if (IsPingMessage(message.payload)) {
+    std::optional<std::string> pong_payload =
+        OnPingPongMessageReceived(message.payload);
+    if (pong_payload.has_value()) {
+      client_->SendMessage(last_sender_id_, *pong_payload, base::DoNothing());
+    } else {
+      LOG(ERROR) << "Failed to generate response for Ping: " << message.payload;
+    }
+  }
 }
 
 void CorpMessagingPlayground::OnCharacterInput(char c) {
@@ -136,6 +171,9 @@
     case '3':
       SendMessage(100);
       break;
+    case '4':
+      StartPingPongMatch();
+      break;
     case 'x':
       run_loop_->Quit();
       break;
@@ -153,4 +191,16 @@
   }
 }
 
+void CorpMessagingPlayground::StartPingPongMatch() {
+  if (last_sender_id_.username.empty()) {
+    LOG(WARNING) << "No message received yet, destination ID is unknown.";
+    return;
+  }
+  LOG(INFO) << "Starting a new Ping-Pong match.";
+  ping_total_rtt_ = {};
+  last_ping_sent_time_ = base::Time::Now();
+  client_->SendMessage(last_sender_id_, CreatePingMessage(1),
+                       base::DoNothing());
+}
+
 }  // namespace remoting
diff --git a/remoting/test/corp_messaging_playground.h b/remoting/test/corp_messaging_playground.h
index 79e4094d..a61d08e 100644
--- a/remoting/test/corp_messaging_playground.h
+++ b/remoting/test/corp_messaging_playground.h
@@ -9,6 +9,7 @@
 
 #include "base/functional/callback_forward.h"
 #include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
 #include "remoting/base/internal_headers.h"
 
 namespace base {
@@ -40,6 +41,7 @@
   void OnSimpleMessageReceived(const internal::SimpleMessageStruct& message);
   void OnCharacterInput(char c);
   void SendMessage(int count = 1);
+  void StartPingPongMatch();
 
   std::unique_ptr<network::TransitionalURLLoaderFactoryOwner>
       url_loader_factory_owner_;
@@ -47,6 +49,8 @@
   std::unique_ptr<base::RunLoop> run_loop_;
   std::unique_ptr<Core> core_;
   internal::EndpointIdStruct last_sender_id_;
+  base::Time last_ping_sent_time_;
+  base::TimeDelta ping_total_rtt_;
   base::WeakPtrFactory<CorpMessagingPlayground> weak_factory_{this};
 };
 
diff --git a/remoting/test/ping_pong_helper.cc b/remoting/test/ping_pong_helper.cc
new file mode 100644
index 0000000..fda5c77
--- /dev/null
+++ b/remoting/test/ping_pong_helper.cc
@@ -0,0 +1,88 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/test/ping_pong_helper.h"
+
+#include <optional>
+#include <string>
+#include <string_view>
+
+#include "base/functional/callback.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/time/time.h"
+#include "remoting/base/internal_headers.h"
+
+namespace remoting {
+
+namespace {
+
+constexpr char kPing[] = "Ping";
+constexpr char kPong[] = "Pong";
+
+}  // namespace
+
+std::optional<std::string> OnPingPongMessageReceived(std::string_view payload) {
+  std::vector<std::string> parts = base::SplitString(
+      payload, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  if (parts.empty()) {
+    return std::nullopt;
+  }
+  if (parts[0] == kPing) {
+    return HandlePing(payload);
+  }
+  if (parts[0] == kPong) {
+    return HandlePong(payload);
+  }
+  return std::nullopt;
+}
+
+std::string CreatePingMessage(int count) {
+  return std::string(kPing) + ":" + base::NumberToString(count);
+}
+
+std::optional<std::string> HandlePing(std::string_view payload) {
+  std::vector<std::string> parts = base::SplitString(
+      payload, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  if (parts.size() != 2) {
+    LOG(WARNING) << "Invalid Ping message format: " << payload;
+    return std::nullopt;
+  }
+
+  return std::string(kPong) + ":" + parts[1];
+}
+
+std::optional<std::string> HandlePong(std::string_view payload) {
+  std::vector<std::string> parts = base::SplitString(
+      payload, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  if (parts.size() != 2) {
+    LOG(WARNING) << "Invalid Pong message format: " << payload;
+    return std::nullopt;
+  }
+
+  int current_count = 0;
+  if (!base::StringToInt(parts[1], &current_count)) {
+    LOG(WARNING) << "Invalid number in Pong message: " << parts[1];
+    return std::nullopt;
+  }
+
+  if (current_count < 10) {
+    return std::string(kPing) + ":" + base::NumberToString(current_count + 1);
+  }
+
+  return std::nullopt;
+}
+
+bool IsPingMessage(std::string_view payload) {
+  return base::SplitString(payload, ":", base::TRIM_WHITESPACE,
+                           base::SPLIT_WANT_ALL)[0] == kPing;
+}
+
+bool IsPongMessage(std::string_view payload) {
+  return base::SplitString(payload, ":", base::TRIM_WHITESPACE,
+                           base::SPLIT_WANT_ALL)[0] == kPong;
+}
+
+}  // namespace remoting
diff --git a/remoting/test/ping_pong_helper.h b/remoting/test/ping_pong_helper.h
new file mode 100644
index 0000000..2a07616
--- /dev/null
+++ b/remoting/test/ping_pong_helper.h
@@ -0,0 +1,42 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_TEST_PING_PONG_HELPER_H_
+#define REMOTING_TEST_PING_PONG_HELPER_H_
+
+#include <optional>
+#include <string>
+#include <string_view>
+
+namespace remoting {
+
+// This file contains a set of helper functions for facilitating a ping-pong
+// match between two endpoints. A ping-pong exchange is initiated by either of
+// the endpoints when they send a "Ping" message with a count N. The receiver
+// will then respond with a "Pong" message, which is then replied to with
+// another "Ping" message, and so on, until the count reaches a limit.
+
+// Dispatches a ping-pong message to the appropriate handler. Returns the
+// payload for a reply if one is needed.
+std::optional<std::string> OnPingPongMessageReceived(std::string_view payload);
+
+// Creates the first "Ping" message to start an exchange.
+std::string CreatePingMessage(int count);
+
+// Handles a "Ping" message and returns the payload for a "Pong" message.
+std::optional<std::string> HandlePing(std::string_view payload);
+
+// Handles a "Pong" message and returns the payload for a "Ping" message if the
+// exchange should continue.
+std::optional<std::string> HandlePong(std::string_view payload);
+
+// Returns true if the message is a "Ping" message.
+bool IsPingMessage(std::string_view payload);
+
+// Returns true if the message is a "Pong" message.
+bool IsPongMessage(std::string_view payload);
+
+}  // namespace remoting
+
+#endif  // REMOTING_TEST_PING_PONG_HELPER_H_
diff --git a/sandbox/win/fuzzer/sandbox_nt_utils_fuzzer.cc b/sandbox/win/fuzzer/sandbox_nt_utils_fuzzer.cc
index 189e2a6..f88acd1 100644
--- a/sandbox/win/fuzzer/sandbox_nt_utils_fuzzer.cc
+++ b/sandbox/win/fuzzer/sandbox_nt_utils_fuzzer.cc
@@ -5,18 +5,21 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <string_view>
-
 #include "sandbox/win/src/nt_internals.h"
 #include "sandbox/win/src/sandbox_nt_util.h"
 
+using ScopedUnicodeString =
+    std::unique_ptr<UNICODE_STRING, sandbox::NtAllocDeleter>;
+
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   if (size % 2 == 1)
     return 0;
 
-  std::wstring_view module_path{
-      reinterpret_cast<wchar_t*>(const_cast<uint8_t*>(data)),
-      size / sizeof(wchar_t)};
-  sandbox::ExtractModuleName(module_path);
+  UNICODE_STRING module_path;
+  module_path.Buffer = reinterpret_cast<wchar_t*>(const_cast<uint8_t*>(data));
+  module_path.Length = size;
+  module_path.MaximumLength = size;
+
+  ScopedUnicodeString result(sandbox::ExtractModuleName(&module_path));
   return 0;
 }
diff --git a/sandbox/win/src/eat_resolver.cc b/sandbox/win/src/eat_resolver.cc
index 24a4b814..b590fd8 100644
--- a/sandbox/win/src/eat_resolver.cc
+++ b/sandbox/win/src/eat_resolver.cc
@@ -19,13 +19,16 @@
 namespace sandbox {
 
 NTSTATUS EatResolverThunk::Setup(const void* target_module,
+                                 const void* interceptor_module,
                                  const char* target_name,
+                                 const char* interceptor_name,
                                  const void* interceptor_entry_point,
                                  void* thunk_storage,
                                  size_t storage_bytes,
                                  size_t* storage_used) {
-  NTSTATUS ret = Init(target_module, target_name, interceptor_entry_point,
-                      thunk_storage, storage_bytes);
+  NTSTATUS ret =
+      Init(target_module, interceptor_module, target_name, interceptor_name,
+           interceptor_entry_point, thunk_storage, storage_bytes);
   if (!NT_SUCCESS(ret))
     return ret;
 
diff --git a/sandbox/win/src/eat_resolver.h b/sandbox/win/src/eat_resolver.h
index b814638..1071aee1 100644
--- a/sandbox/win/src/eat_resolver.h
+++ b/sandbox/win/src/eat_resolver.h
@@ -25,7 +25,9 @@
 
   // Implementation of Resolver::Setup.
   NTSTATUS Setup(const void* target_module,
+                 const void* interceptor_module,
                  const char* target_name,
+                 const char* interceptor_name,
                  const void* interceptor_entry_point,
                  void* thunk_storage,
                  size_t storage_bytes,
diff --git a/sandbox/win/src/interception.cc b/sandbox/win/src/interception.cc
index b2a0619..6af0ac9 100644
--- a/sandbox/win/src/interception.cc
+++ b/sandbox/win/src/interception.cc
@@ -85,7 +85,7 @@
 InterceptionManager::InterceptionData::~InterceptionData() {}
 
 InterceptionManager::InterceptionManager(TargetProcess& child_process)
-    : child_(child_process) {}
+    : child_(child_process), names_used_(false) {}
 InterceptionManager::~InterceptionManager() {}
 
 bool InterceptionManager::AddToPatchedFunctions(
@@ -105,6 +105,25 @@
   return true;
 }
 
+bool InterceptionManager::AddToPatchedFunctions(
+    const wchar_t* dll_name,
+    const char* function_name,
+    InterceptionType interception_type,
+    const char* replacement_function_name,
+    InterceptorId id) {
+  InterceptionData function;
+  function.type = interception_type;
+  function.id = id;
+  function.dll = dll_name;
+  function.function = function_name;
+  function.interceptor = replacement_function_name;
+  function.interceptor_address = nullptr;
+
+  interceptions_.push_back(function);
+  names_used_ = true;
+  return true;
+}
+
 bool InterceptionManager::AddToUnloadModules(const wchar_t* dll_name) {
   InterceptionData module_to_unload;
   module_to_unload.type = INTERCEPTION_UNLOAD_MODULE;
@@ -163,7 +182,8 @@
     }
 
     // we have to NULL terminate the strings on the structure
-    size_t strings_chars = interception.function.size() + 1;
+    size_t strings_chars =
+        interception.function.size() + interception.interceptor.size() + 2;
 
     // a new FunctionInfo is required per function
     size_t record_bytes = offsetof(FunctionInfo, function) + strings_chars;
@@ -191,6 +211,9 @@
   DllPatchInfo* dll_info = shared_memory->dll_list;
   size_t num_dlls = 0;
 
+  shared_memory->interceptor_base =
+      names_used_ ? child_->MainModule() : nullptr;
+
   buffer_bytes -= offsetof(SharedMemory, dll_list);
   buffer = dll_info;
 
@@ -255,7 +278,6 @@
   dll_info->record_bytes = required;
   dll_info->offset_to_functions = required;
   dll_info->num_functions = 0;
-  dll_info->dll_name_len = data.dll.size();
   data.dll.copy(dll_info->dll_name, data.dll.size());
   dll_info->dll_name[data.dll.size()] = L'\0';
 
@@ -278,9 +300,11 @@
   FunctionInfo* function = reinterpret_cast<FunctionInfo*>(*buffer);
 
   size_t name_bytes = data.function.size();
+  size_t interceptor_bytes = data.interceptor.size();
 
   // the strings at the end of the structure are zero terminated
-  size_t required = offsetof(FunctionInfo, function) + name_bytes + 1;
+  size_t required =
+      offsetof(FunctionInfo, function) + name_bytes + interceptor_bytes + 2;
   required = RoundUpToMultiple(required, sizeof(size_t));
   if (*buffer_bytes < required)
     return false;
@@ -293,11 +317,16 @@
   function->type = data.type;
   function->id = data.id;
   function->interceptor_address = data.interceptor_address;
-  char* name = function->function;
+  char* names = function->function;
 
-  data.function.copy(name, name_bytes);
-  name += name_bytes;
-  *name++ = '\0';
+  data.function.copy(names, name_bytes);
+  names += name_bytes;
+  *names++ = '\0';
+
+  // interceptor follows the function_name
+  data.interceptor.copy(names, interceptor_bytes);
+  names += interceptor_bytes;
+  *names++ = '\0';
 
   // update the dll table
   dll_info->num_functions++;
@@ -411,11 +440,11 @@
     if (INTERCEPTION_SERVICE_CALL != interception.type)
       return base::unexpected(SBOX_ERROR_BAD_PARAMS);
 
-    NTSTATUS ret =
-        thunk.Setup(ntdll_base, interception.function.c_str(),
-                    interception.interceptor_address,
-                    &thunks->thunks[patch.dll_data.num_thunks],
-                    thunk_bytes - patch.dll_data.used_bytes, nullptr);
+    NTSTATUS ret = thunk.Setup(
+        ntdll_base, nullptr, interception.function.c_str(),
+        interception.interceptor.c_str(), interception.interceptor_address,
+        &thunks->thunks[patch.dll_data.num_thunks],
+        thunk_bytes - patch.dll_data.used_bytes, nullptr);
     if (!NT_SUCCESS(ret)) {
       ::SetLastError(GetLastErrorFromNtStatus(ret));
       return base::unexpected(SBOX_ERROR_CANNOT_SETUP_INTERCEPTION_THUNK);
diff --git a/sandbox/win/src/interception.h b/sandbox/win/src/interception.h
index 25ed87c..dc9c4cba 100644
--- a/sandbox/win/src/interception.h
+++ b/sandbox/win/src/interception.h
@@ -113,6 +113,14 @@
                              const void* replacement_code_address,
                              InterceptorId id);
 
+  // Patches function_name inside dll_name to point to
+  // replacement_function_name.
+  bool AddToPatchedFunctions(const wchar_t* dll_name,
+                             const char* function_name,
+                             InterceptionType interception_type,
+                             const char* replacement_function_name,
+                             InterceptorId id);
+
   // The interception agent will unload the dll with dll_name.
   bool AddToUnloadModules(const wchar_t* dll_name);
 
@@ -139,6 +147,7 @@
     InterceptorId id;                 // Interceptor id.
     std::wstring dll;                 // Name of dll to intercept.
     std::string function;             // Name of function to intercept.
+    std::string interceptor;          // Name of interceptor function.
     // Interceptor's entry point. Not a raw_ptr<> as it will always point at
     // a function like `TargetNtOpenThread64`.
     RAW_PTR_EXCLUSION const void* interceptor_address;
@@ -200,6 +209,9 @@
   // Holds all interception info until the call to initialize (perform the
   // actual patch).
   std::list<InterceptionData> interceptions_;
+
+  // Keep track of patches added by name.
+  bool names_used_;
 };
 
 // This macro simply calls interception_manager.AddToPatchedFunctions with
diff --git a/sandbox/win/src/interception_agent.cc b/sandbox/win/src/interception_agent.cc
index 3325e07b..519b686 100644
--- a/sandbox/win/src/interception_agent.cc
+++ b/sandbox/win/src/interception_agent.cc
@@ -21,8 +21,6 @@
 #include "sandbox/win/src/interceptors.h"
 #include "sandbox/win/src/sandbox_nt_util.h"
 
-namespace sandbox {
-
 namespace {
 
 // Returns true if target lies between base and base + range.
@@ -31,19 +29,10 @@
   return reinterpret_cast<const char*>(target) < end;
 }
 
-bool CompareNames(std::wstring_view current_name, std::wstring_view name) {
-  if (current_name.empty()) {
-    return false;
-  }
-  auto result = EqualUnicodeString(current_name, name);
-  if (!result) {
-    return false;
-  }
-  return *result;
-}
-
 }  // namespace
 
+namespace sandbox {
+
 // The list of intercepted functions back-pointers.
 SANDBOX_INTERCEPT OriginalFunctions g_originals;
 
@@ -76,17 +65,33 @@
   return true;
 }
 
-bool InterceptionAgent::OnDllLoad(std::wstring_view full_path,
-                                  std::wstring_view name,
+bool InterceptionAgent::DllMatch(const UNICODE_STRING* full_path,
+                                 const UNICODE_STRING* name,
+                                 const DllPatchInfo* dll_info) {
+  UNICODE_STRING current_name;
+  current_name.Length = static_cast<USHORT>(
+      GetNtExports()->wcslen(dll_info->dll_name) * sizeof(wchar_t));
+  current_name.MaximumLength = current_name.Length;
+  current_name.Buffer = const_cast<wchar_t*>(dll_info->dll_name);
+
+  BOOLEAN case_insensitive = TRUE;
+  if (full_path && !GetNtExports()->RtlCompareUnicodeString(
+                       &current_name, full_path, case_insensitive)) {
+    return true;
+  }
+  return name && !GetNtExports()->RtlCompareUnicodeString(&current_name, name,
+                                                          case_insensitive);
+}
+
+bool InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path,
+                                  const UNICODE_STRING* name,
                                   void* base_address) {
   DllPatchInfo* dll_info = interceptions_->dll_list;
   size_t i = 0;
   for (; i < interceptions_->num_intercepted_dlls; i++) {
-    std::wstring_view current_name(dll_info->dll_name, dll_info->dll_name_len);
-    if (CompareNames(current_name, full_path) ||
-        CompareNames(current_name, name)) {
+    if (DllMatch(full_path, name, dll_info))
       break;
-    }
+
     dll_info = reinterpret_cast<DllPatchInfo*>(
         reinterpret_cast<char*>(dll_info) + dll_info->record_bytes);
   }
@@ -160,9 +165,19 @@
     if (!resolver)
       return false;
 
+    const char* interceptor =
+        function->function + GetNtExports()->strlen(function->function) + 1;
+
+    if (!IsWithinRange(function, function->record_bytes, interceptor) ||
+        !IsWithinRange(dll_info, dll_info->record_bytes, interceptor)) {
+      NOTREACHED_NT();
+      return false;
+    }
+
     NTSTATUS ret = resolver->Setup(
-        thunks->base, function->function, function->interceptor_address,
-        &thunks->thunks[i], sizeof(ThunkData), nullptr);
+        thunks->base, interceptions_->interceptor_base, function->function,
+        interceptor, function->interceptor_address, &thunks->thunks[i],
+        sizeof(ThunkData), nullptr);
     if (!NT_SUCCESS(ret)) {
       NOTREACHED_NT();
       return false;
diff --git a/sandbox/win/src/interception_agent.h b/sandbox/win/src/interception_agent.h
index 4ea734d..dd48a13 100644
--- a/sandbox/win/src/interception_agent.h
+++ b/sandbox/win/src/interception_agent.h
@@ -10,9 +10,8 @@
 #define SANDBOX_WIN_SRC_INTERCEPTION_AGENT_H_
 
 #include <windows.h>
-#include <winternl.h>
 
-#include <string_view>
+#include <winternl.h>
 
 #include "base/memory/raw_ptr_exclusion.h"
 #include "sandbox/win/src/sandbox_types.h"
@@ -54,8 +53,7 @@
   // full_path is the (optional) full name of the module being loaded and name
   // is the internal module name. If full_path is provided, it will be used
   // before the internal name to determine if we care about this dll.
-  bool OnDllLoad(std::wstring_view full_path,
-                 std::wstring_view name,
+  bool OnDllLoad(const UNICODE_STRING* full_path, const UNICODE_STRING* name,
                  void* base_address);
 
   // Performs cleanup when a dll is unloaded.
@@ -67,6 +65,11 @@
   // Performs initialization of the singleton.
   bool Init(SharedMemory* shared_memory);
 
+  // Returns true if we are interested on this dll. dll_info is an entry of the
+  // list of intercepted dlls.
+  bool DllMatch(const UNICODE_STRING* full_path, const UNICODE_STRING* name,
+                const DllPatchInfo* dll_info);
+
   // Performs the patching of the dll loaded at base_address.
   // The patches to perform are described on dll_info, and thunks is the thunk
   // storage for the whole dll.
diff --git a/sandbox/win/src/interception_internal.h b/sandbox/win/src/interception_internal.h
index 61ebb31..39cb695c 100644
--- a/sandbox/win/src/interception_internal.h
+++ b/sandbox/win/src/interception_internal.h
@@ -35,6 +35,7 @@
   // Not a raw_ptr<> as this represents an address in another process.
   RAW_PTR_EXCLUSION const void* interceptor_address;
   char function[1];  // placeholder for null terminated name
+  // char interceptor[]           // followed by the interceptor function
 };
 
 // A single dll:
@@ -43,7 +44,6 @@
   size_t offset_to_functions;
   size_t num_functions;
   bool unload_module;
-  size_t dll_name_len;
   wchar_t dll_name[1];  // placeholder for null terminated name
   // FunctionInfo function_info[] // followed by the functions to intercept
 };
@@ -51,6 +51,8 @@
 // All interceptions:
 struct SharedMemory {
   size_t num_intercepted_dlls;
+  // Not a raw_ptr<> as this represents an address in another process.
+  RAW_PTR_EXCLUSION void* interceptor_base;
   DllPatchInfo dll_list[1];  // placeholder for the list of dlls
 };
 
diff --git a/sandbox/win/src/interception_unittest.cc b/sandbox/win/src/interception_unittest.cc
index 0f50a49..208330f6 100644
--- a/sandbox/win/src/interception_unittest.cc
+++ b/sandbox/win/src/interception_unittest.cc
@@ -39,17 +39,22 @@
 // buffer (in): the buffer to walk.
 // num_dlls (out): count of the dlls on the buffer.
 // num_function (out): count of intercepted functions.
-void WalkBuffer(base::span<BYTE> buffer, int* num_dlls, int* num_functions) {
+// num_names (out): count of named interceptor functions.
+void WalkBuffer(base::span<BYTE> buffer,
+                int* num_dlls,
+                int* num_functions,
+                int* num_names) {
   ASSERT_TRUE(buffer.data());
   ASSERT_TRUE(num_functions);
-  *num_dlls = *num_functions = 0;
+  ASSERT_TRUE(num_names);
+  *num_dlls = *num_functions = *num_names = 0;
   SharedMemory* memory = reinterpret_cast<SharedMemory*>(buffer.data());
 
   ASSERT_GT(buffer.size(), sizeof(SharedMemory));
   DllPatchInfo* dll = &memory->dll_list[0];
 
   for (size_t i = 0; i < memory->num_intercepted_dlls; i++) {
-    ASSERT_EQ(dll->dll_name_len, wcslen(dll->dll_name));
+    ASSERT_NE(0u, wcslen(dll->dll_name));
     ASSERT_EQ(0u, dll->record_bytes % sizeof(size_t));
     ASSERT_EQ(0u, dll->offset_to_functions % sizeof(size_t));
     ASSERT_NE(0u, dll->num_functions);
@@ -63,11 +68,19 @@
       char* name = function->function;
       size_t length = strlen(name);
       ASSERT_NE(0u, length);
+      name += length + 1;
 
       // look for overflows
       ASSERT_GT(reinterpret_cast<char*>(buffer.data()) + buffer.size(),
-                name + length);
-      EXPECT_TRUE(function->interceptor_address);
+                name + strlen(name));
+
+      // look for a named interceptor
+      if (strlen(name)) {
+        (*num_names)++;
+        EXPECT_TRUE(!function->interceptor_address);
+      } else {
+        EXPECT_TRUE(function->interceptor_address);
+      }
 
       (*num_functions)++;
       function = reinterpret_cast<FunctionInfo*>(
@@ -142,6 +155,9 @@
                                       INTERCEPTION_EAT, function, OPEN_KEY_ID);
   interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg",
                                       INTERCEPTION_EAT, function, OPEN_KEY_ID);
+  interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg",
+                                      INTERCEPTION_EAT, "replacement",
+                                      OPEN_KEY_ID);
   interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
                                       INTERCEPTION_EAT, function, OPEN_KEY_ID);
   interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtClose",
@@ -149,6 +165,10 @@
                                       OPEN_KEY_ID);
   interceptions.AddToPatchedFunctions(L"some.dll", "Superfn", INTERCEPTION_EAT,
                                       function, OPEN_KEY_ID);
+  interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+                                      INTERCEPTION_EAT, "a", OPEN_KEY_ID);
+  interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+                                      INTERCEPTION_EAT, "abc", OPEN_KEY_ID);
   interceptions.AddToPatchedFunctions(L"a.dll", "p", INTERCEPTION_EAT, function,
                                       OPEN_KEY_ID);
   interceptions.AddToPatchedFunctions(L"b.dll",
@@ -160,7 +180,7 @@
                                       function, OPEN_KEY_ID);
 
   // Verify that all interceptions were added
-  ASSERT_EQ(12u, interceptions.interceptions_.size());
+  ASSERT_EQ(15u, interceptions.interceptions_.size());
 
   auto local_buffer =
       base::HeapArray<BYTE>::Uninit(interceptions.GetBufferSize());
@@ -175,16 +195,18 @@
   // patched on the client. The second group lives on local_buffer, and the
   // first group remains on the list of interceptions (inside the object
   // "interceptions"). There are 2 local interceptions (of ntdll); the
-  // other 10 have to be sent to the child to be performed "hot".
+  // other 13 have to be sent to the child to be performed "hot".
   EXPECT_EQ(2u, interceptions.interceptions_.size());
 
-  int num_dlls, num_functions;
-  WalkBuffer(local_buffer, &num_dlls, &num_functions);
+  int num_dlls, num_functions, num_names;
+  WalkBuffer(local_buffer, &num_dlls, &num_functions, &num_names);
 
-  // The 10 interceptions on the buffer (to the child) should be grouped on 6
-  // dlls.
+  // The 13 interceptions on the buffer (to the child) should be grouped on 6
+  // dlls. Only four interceptions are using an explicit name for the
+  // interceptor function.
   EXPECT_EQ(6, num_dlls);
-  EXPECT_EQ(10, num_functions);
+  EXPECT_EQ(13, num_functions);
+  EXPECT_EQ(3, num_names);
 }
 
 TEST(InterceptionManagerTest, BufferLayout2) {
@@ -222,11 +244,12 @@
   // first group remains on the list of interceptions, in this case just one.
   EXPECT_EQ(1u, interceptions.interceptions_.size());
 
-  int num_dlls, num_functions;
-  WalkBuffer(local_buffer, &num_dlls, &num_functions);
+  int num_dlls, num_functions, num_names;
+  WalkBuffer(local_buffer, &num_dlls, &num_functions, &num_names);
 
   EXPECT_EQ(3, num_dlls);
   EXPECT_EQ(3, num_functions);
+  EXPECT_EQ(0, num_names);
 }
 
 }  // namespace sandbox
diff --git a/sandbox/win/src/nt_internals.h b/sandbox/win/src/nt_internals.h
index 91feec41..8df2c68 100644
--- a/sandbox/win/src/nt_internals.h
+++ b/sandbox/win/src/nt_internals.h
@@ -361,6 +361,10 @@
                                         IN const char* _Str2,
                                         IN size_t _MaxCount);
 
+typedef size_t(__cdecl* strlenFunction)(IN const char* _Str);
+
+typedef size_t(__cdecl* wcslenFunction)(IN const wchar_t* _Str);
+
 typedef void*(__cdecl* memcpyFunction)(IN void* dest,
                                        IN const void* src,
                                        IN size_t count);
@@ -370,10 +374,6 @@
     IN PANSI_STRING SourceString,
     IN BOOLEAN AllocateDestinationString);
 
-typedef VOID(WINAPI* RtlInitAnsiStringFunction)(OUT PANSI_STRING
-                                                    DestinationString,
-                                                IN OPTIONAL PCSZ SourceString);
-
 typedef LONG(WINAPI* RtlCompareUnicodeStringFunction)(
     IN PCUNICODE_STRING String1,
     IN PCUNICODE_STRING String2,
diff --git a/sandbox/win/src/resolver.cc b/sandbox/win/src/resolver.cc
index 05804ff..b48a2f2 100644
--- a/sandbox/win/src/resolver.cc
+++ b/sandbox/win/src/resolver.cc
@@ -15,19 +15,26 @@
 namespace sandbox {
 
 NTSTATUS ResolverThunk::Init(const void* target_module,
+                             const void* interceptor_module,
                              const char* target_name,
+                             const char* interceptor_name,
                              const void* interceptor_entry_point,
                              void* thunk_storage,
                              size_t storage_bytes) {
-  if (!thunk_storage || 0 == storage_bytes || !target_module || !target_name ||
-      !interceptor_entry_point) {
+  if (!thunk_storage || 0 == storage_bytes || !target_module || !target_name)
     return STATUS_INVALID_PARAMETER;
-  }
 
   if (storage_bytes < GetThunkSize())
     return STATUS_BUFFER_TOO_SMALL;
 
   NTSTATUS ret = STATUS_SUCCESS;
+  if (!interceptor_entry_point) {
+    ret = ResolveInterceptor(interceptor_module, interceptor_name,
+                             &interceptor_entry_point);
+    if (!NT_SUCCESS(ret))
+      return ret;
+  }
+
   ret = ResolveTarget(target_module, target_name, &target_);
   if (!NT_SUCCESS(ret))
     return ret;
@@ -37,4 +44,22 @@
   return ret;
 }
 
+NTSTATUS ResolverThunk::ResolveInterceptor(const void* interceptor_module,
+                                           const char* interceptor_name,
+                                           const void** address) {
+  if (!interceptor_module)
+    return STATUS_INVALID_PARAMETER;
+
+  base::win::PEImage pe(interceptor_module);
+  if (!pe.VerifyMagic())
+    return STATUS_INVALID_IMAGE_FORMAT;
+
+  *address = reinterpret_cast<void*>(pe.GetProcAddress(interceptor_name));
+
+  if (!(*address))
+    return STATUS_PROCEDURE_NOT_FOUND;
+
+  return STATUS_SUCCESS;
+}
+
 }  // namespace sandbox
diff --git a/sandbox/win/src/resolver.h b/sandbox/win/src/resolver.h
index 84e8334..d1e352c 100644
--- a/sandbox/win/src/resolver.h
+++ b/sandbox/win/src/resolver.h
@@ -30,7 +30,9 @@
 
   // Performs the actual interception of a function.
   // target_name is an exported function from the module loaded at
-  // target_module, and must be replaced by interceptor_entry_point.
+  // target_module, and must be replaced by interceptor_name, exported from
+  // interceptor_module. interceptor_entry_point can be provided instead of
+  // interceptor_name / interceptor_module.
   // thunk_storage must point to a buffer on the child's address space, to hold
   // the patch thunk, and related data. If provided, storage_used will receive
   // the number of bytes used from thunk_storage.
@@ -40,24 +42,31 @@
   // size_t size = resolver.GetThunkSize();
   // char* buffer = ::VirtualAllocEx(child_process, nullptr, size,
   //                                 MEM_COMMIT, PAGE_READWRITE);
-  // resolver.Setup(ntdll_module, L"NtCreateFile", &MyReplacementFunction, buffer, size, 
-  //                nullptr);
+  // resolver.Setup(ntdll_module, nullptr, L"NtCreateFile", nullptr,
+  //                &MyReplacementFunction, buffer, size, nullptr);
   //
   // In general, the idea is to allocate a single big buffer for all
   // interceptions on the same dll, and call Setup n times.
   // WARNING: This means that any data member that is specific to a single
   // interception must be reset within this method.
   virtual NTSTATUS Setup(const void* target_module,
+                         const void* interceptor_module,
                          const char* target_name,
+                         const char* interceptor_name,
                          const void* interceptor_entry_point,
                          void* thunk_storage,
                          size_t storage_bytes,
                          size_t* storage_used) = 0;
 
+  // Gets the address of function_name inside module (main exe).
+  virtual NTSTATUS ResolveInterceptor(const void* module,
+                                      const char* function_name,
+                                      const void** address);
+
   // Gets the address of an exported function_name inside module.
   virtual NTSTATUS ResolveTarget(const void* module,
                                  const char* function_name,
-                                 void** address) = 0;
+                                 void** address);
 
   // Gets the required buffer size for this type of thunk.
   virtual size_t GetThunkSize() const = 0;
@@ -68,11 +77,15 @@
   // and the interceptor into the member variables.
   //
   // target_name is an exported function from the module loaded at
-  // target_module, and must be replaced by interceptor_entry_point.
+  // target_module, and must be replaced by interceptor_name, exported from
+  // interceptor_module. interceptor_entry_point can be provided instead of
+  // interceptor_name / interceptor_module.
   // thunk_storage must point to a buffer on the child's address space, to hold
   // the patch thunk, and related data.
   virtual NTSTATUS Init(const void* target_module,
+                        const void* interceptor_module,
                         const char* target_name,
+                        const char* interceptor_name,
                         const void* interceptor_entry_point,
                         void* thunk_storage,
                         size_t storage_bytes);
diff --git a/sandbox/win/src/resolver_32.cc b/sandbox/win/src/resolver_32.cc
index cf85a31..1c34907 100644
--- a/sandbox/win/src/resolver_32.cc
+++ b/sandbox/win/src/resolver_32.cc
@@ -85,4 +85,11 @@
   return sizeof(InternalThunk);
 }
 
+NTSTATUS ResolverThunk::ResolveTarget(const void* module,
+                                      const char* function_name,
+                                      void** address) {
+  const void** casted = const_cast<const void**>(address);
+  return ResolverThunk::ResolveInterceptor(module, function_name, casted);
+}
+
 }  // namespace sandbox
diff --git a/sandbox/win/src/resolver_64.cc b/sandbox/win/src/resolver_64.cc
index 5de26174..bf39e219 100644
--- a/sandbox/win/src/resolver_64.cc
+++ b/sandbox/win/src/resolver_64.cc
@@ -86,4 +86,11 @@
   return true;
 }
 
+NTSTATUS ResolverThunk::ResolveTarget(const void* module,
+                                      const char* function_name,
+                                      void** address) {
+  // We don't support sidestep & co.
+  return STATUS_NOT_IMPLEMENTED;
+}
+
 }  // namespace sandbox
diff --git a/sandbox/win/src/sandbox_nt_types.h b/sandbox/win/src/sandbox_nt_types.h
index d82b41f..30634f1 100644
--- a/sandbox/win/src/sandbox_nt_types.h
+++ b/sandbox/win/src/sandbox_nt_types.h
@@ -35,13 +35,14 @@
   NtWaitForSingleObjectFunction          WaitForSingleObject;
   RtlAllocateHeapFunction                RtlAllocateHeap;
   RtlAnsiStringToUnicodeStringFunction   RtlAnsiStringToUnicodeString;
-  RtlInitAnsiStringFunction              RtlInitAnsiString;
   RtlCompareUnicodeStringFunction        RtlCompareUnicodeString;
   RtlCreateHeapFunction                  RtlCreateHeap;
   RtlDestroyHeapFunction                 RtlDestroyHeap;
   RtlFreeHeapFunction                    RtlFreeHeap;
   RtlNtStatusToDosErrorFunction          RtlNtStatusToDosError;
   UNSAFE_BUFFER_USAGE _strnicmpFunction  _strnicmp;
+  UNSAFE_BUFFER_USAGE strlenFunction     strlen;
+  UNSAFE_BUFFER_USAGE wcslenFunction     wcslen;
   UNSAFE_BUFFER_USAGE memcpyFunction     memcpy;
 };
 // clang-format on
diff --git a/sandbox/win/src/sandbox_nt_util.cc b/sandbox/win/src/sandbox_nt_util.cc
index 7857bc9..1377ca4 100644
--- a/sandbox/win/src/sandbox_nt_util.cc
+++ b/sandbox/win/src/sandbox_nt_util.cc
@@ -19,6 +19,7 @@
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
 #include "base/win/pe_image.h"
+#include "base/win/win_util.h"
 #include "sandbox/win/src/internal_types.h"
 #include "sandbox/win/src/nt_internals.h"
 #include "sandbox/win/src/sandbox_factory.h"
@@ -160,13 +161,14 @@
   INIT_NT(WaitForSingleObject);
   INIT_RTL(RtlAllocateHeap);
   INIT_RTL(RtlAnsiStringToUnicodeString);
-  INIT_RTL(RtlInitAnsiString);
   INIT_RTL(RtlCompareUnicodeString);
   INIT_RTL(RtlCreateHeap);
   INIT_RTL(RtlDestroyHeap);
   INIT_RTL(RtlFreeHeap);
   INIT_RTL(RtlNtStatusToDosError);
   INIT_RTL(_strnicmp);
+  INIT_RTL(strlen);
+  INIT_RTL(wcslen);
   INIT_RTL(memcpy);
   sandbox::g_nt.Initialized = true;
 }
@@ -186,18 +188,6 @@
 static_assert(offsetof(PARTIAL_TEB, ProcessEnvironmentBlock) ==
               offsetof(TEB, ProcessEnvironmentBlock));
 
-// Can't use the base::win version in component builds so just repeat it.
-bool ViewToUnicodeString(std::wstring_view str, UNICODE_STRING& ustr) {
-  constexpr size_t kMaxLength = UINT16_MAX / sizeof(WCHAR);
-  if (std::size(str) > kMaxLength) {
-    return false;
-  }
-  ustr.Buffer = const_cast<WCHAR*>(str.data());
-  ustr.Length = static_cast<USHORT>(std::size(str) * sizeof(WCHAR));
-  ustr.MaximumLength = ustr.Length;
-  return true;
-}
-
 }  // namespace.
 
 namespace sandbox {
@@ -253,13 +243,6 @@
   return true;
 }
 
-ScopedUnicodeString::ScopedUnicodeString() = default;
-ScopedUnicodeString::~ScopedUnicodeString() = default;
-ScopedUnicodeString::ScopedUnicodeString(
-    std::unique_ptr<uint8_t, NtAllocDeleter> buffer,
-    std::wstring_view str)
-    : buffer_(std::move(buffer)), str_(str) {}
-
 void* GetGlobalIPCMemory() {
   if (!MapGlobalMemory())
     return nullptr;
@@ -339,6 +322,16 @@
   return true;
 }
 
+NTSTATUS CopyData(void* destination, const void* source, size_t bytes) {
+  NTSTATUS ret = STATUS_SUCCESS;
+  __try {
+    GetNtExports()->memcpy(destination, source, bytes);
+  } __except (EXCEPTION_EXECUTE_HANDLER) {
+    ret = (NTSTATUS)GetExceptionCode();
+  }
+  return ret;
+}
+
 NTSTATUS GetProcessId(HANDLE process, DWORD* process_id) {
   PROCESS_BASIC_INFORMATION proc_info;
   ULONG bytes_returned;
@@ -423,58 +416,70 @@
   return true;
 }
 
-ScopedUnicodeString AnsiToUnicode(const char* string) {
-  STACK_UNINITIALIZED ANSI_STRING ansi_string;
-  GetNtExports()->RtlInitAnsiString(&ansi_string, string);
+UNICODE_STRING* AnsiToUnicode(const char* string) {
+  ANSI_STRING ansi_string;
+  ansi_string.Length = static_cast<USHORT>(GetNtExports()->strlen(string));
+  ansi_string.MaximumLength = ansi_string.Length + 1;
+  ansi_string.Buffer = const_cast<char*>(string);
 
-  size_t name_bytes = ansi_string.MaximumLength * sizeof(wchar_t);
+  if (ansi_string.Length > ansi_string.MaximumLength)
+    return nullptr;
 
-  auto buffer = std::unique_ptr<uint8_t, NtAllocDeleter>(
-      new (NT_ALLOC) uint8_t[name_bytes]);
-  if (!buffer) {
-    return {};
-  }
+  size_t name_bytes =
+      ansi_string.MaximumLength * sizeof(wchar_t) + sizeof(UNICODE_STRING);
 
-  STACK_UNINITIALIZED UNICODE_STRING out_string;
-  out_string.Length = 0;
-  out_string.MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t);
-  out_string.Buffer = reinterpret_cast<wchar_t*>(buffer.get());
+  UNICODE_STRING* out_string =
+      reinterpret_cast<UNICODE_STRING*>(new (NT_ALLOC) char[name_bytes]);
+  if (!out_string)
+    return nullptr;
 
+  out_string->MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t);
+  out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
+
+  BOOLEAN alloc_destination = false;
   NTSTATUS ret = GetNtExports()->RtlAnsiStringToUnicodeString(
-      &out_string, &ansi_string, FALSE);
+      out_string, &ansi_string, alloc_destination);
   DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret);
   if (!NT_SUCCESS(ret)) {
-    return {};
+    operator delete(out_string, NT_ALLOC);
+    return nullptr;
   }
 
-  return {std::move(buffer),
-          std::wstring_view(out_string.Buffer,
-                            out_string.Length / sizeof(wchar_t))};
+  return out_string;
 }
 
-ScopedUnicodeString GetImageInfoFromModule(HMODULE module, bool* has_code) {
+UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32_t* flags) {
 // PEImage's dtor won't be run during SEH unwinding, but that's OK.
 #pragma warning(push)
 #pragma warning(disable : 4509)
+  UNICODE_STRING* out_name = nullptr;
   __try {
-    *has_code = false;
-    base::win::PEImage pe(module);
+    do {
+      *flags = 0;
+      base::win::PEImage pe(module);
 
-    if (!pe.VerifyMagic()) {
-      return {};
-    }
-    PIMAGE_NT_HEADERS headers = pe.GetNTHeaders();
-    *has_code = headers && !!headers->OptionalHeader.SizeOfCode;
+      if (!pe.VerifyMagic())
+        break;
+      *flags |= MODULE_IS_PE_IMAGE;
 
-    PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
-    if (exports) {
-      char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name));
-      return AnsiToUnicode(name);
-    }
+      PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
+      if (exports) {
+        char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name));
+        out_name = AnsiToUnicode(name);
+      }
+
+      PIMAGE_NT_HEADERS headers = pe.GetNTHeaders();
+      if (headers) {
+        if (headers->OptionalHeader.AddressOfEntryPoint)
+          *flags |= MODULE_HAS_ENTRY_POINT;
+        if (headers->OptionalHeader.SizeOfCode)
+          *flags |= MODULE_HAS_CODE;
+      }
+    } while (false);
   } __except (EXCEPTION_EXECUTE_HANDLER) {
   }
 
-  return {};
+  return out_name;
 #pragma warning(pop)
 }
 
@@ -482,74 +487,109 @@
 // PEImage's dtor won't be run during SEH unwinding, but that's OK.
 #pragma warning(push)
 #pragma warning(disable : 4509)
+  const char* out_name = nullptr;
   __try {
+    do {
       base::win::PEImage pe(module);
 
-      if (!pe.VerifyMagic()) {
-        return nullptr;
-      }
+      if (!pe.VerifyMagic())
+        break;
 
       PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
-      if (exports) {
-        return static_cast<const char*>(pe.RVAToAddr(exports->Name));
-      }
+      if (exports)
+        out_name = static_cast<const char*>(pe.RVAToAddr(exports->Name));
+    } while (false);
   } __except (EXCEPTION_EXECUTE_HANDLER) {
   }
 
-  return nullptr;
+  return out_name;
 #pragma warning(pop)
 }
 
-ScopedUnicodeString GetBackingFilePath(PVOID address) {
+UNICODE_STRING* GetBackingFilePath(PVOID address) {
   // We'll start with something close to max_path charactes for the name.
   SIZE_T buffer_bytes = MAX_PATH * 2;
 
   for (;;) {
-    std::unique_ptr<uint8_t, NtAllocDeleter> section_name(
-        new (NT_ALLOC) uint8_t[buffer_bytes]);
-    if (!section_name) {
-      return {};
-    }
+    MEMORY_SECTION_NAME* section_name = reinterpret_cast<MEMORY_SECTION_NAME*>(
+        new (NT_ALLOC) char[buffer_bytes]);
+
+    if (!section_name)
+      return nullptr;
 
     SIZE_T returned_bytes;
     NTSTATUS ret = GetNtExports()->QueryVirtualMemory(
-        NtCurrentProcess, address, MemorySectionName, section_name.get(),
+        NtCurrentProcess, address, MemorySectionName, section_name,
         buffer_bytes, &returned_bytes);
 
     if (STATUS_BUFFER_OVERFLOW == ret) {
       // Retry the call with the given buffer size.
+      operator delete(section_name, NT_ALLOC);
+      section_name = nullptr;
       buffer_bytes = returned_bytes;
       continue;
     }
     if (!NT_SUCCESS(ret)) {
-      return {};
+      operator delete(section_name, NT_ALLOC);
+      return nullptr;
     }
 
-    UNICODE_STRING* str = reinterpret_cast<UNICODE_STRING*>(section_name.get());
-    return {std::move(section_name),
-            std::wstring_view(str->Buffer, str->Length / sizeof(wchar_t))};
+    return reinterpret_cast<UNICODE_STRING*>(section_name);
   }
 }
 
-std::wstring_view ExtractModuleName(std::wstring_view module_path) {
-  if (module_path.empty()) {
-    return {};
+UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) {
+  if ((!module_path) || (!module_path->Buffer))
+    return nullptr;
+
+  wchar_t* start_ptr = &module_path->Buffer[0];
+  if (module_path->Length > 0) {
+    size_t last_char = module_path->Length / sizeof(wchar_t) - 1;
+    // Ends with path separator. Not a valid module name.
+    if (module_path->Buffer[last_char] == L'\\')
+      return nullptr;
+    // Search backwards for path separator.
+    for (size_t i = 0; i <= last_char; ++i) {
+      if (module_path->Buffer[last_char - i] == L'\\') {
+        start_ptr = &module_path->Buffer[last_char - i + 1];
+        break;
+      }
+    }
   }
-  auto last_slash = module_path.rfind(L'\\');
-  // If the string ends with path separator then this will return an empty
-  // string which signifies no module name.
-  if (last_slash != std::wstring_view::npos) {
-    module_path.remove_prefix(last_slash + 1);
+
+  size_t skip_bytes = reinterpret_cast<uintptr_t>(start_ptr) -
+                      reinterpret_cast<uintptr_t>(&module_path->Buffer[0]);
+  // We add a nul wchar to the buffer.
+  size_t size_bytes = module_path->Length - skip_bytes + sizeof(wchar_t);
+
+  // Because module_path is a UNICODE_STRING, size_bytes will be small enough
+  // to make the static_cast below safe.
+  DCHECK_NT(UINT16_MAX > size_bytes);
+  char* str_buffer = new (NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)];
+  if (!str_buffer)
+    return nullptr;
+
+  UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(str_buffer);
+  out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
+  out_string->Length = static_cast<USHORT>(size_bytes - sizeof(wchar_t));
+  out_string->MaximumLength = static_cast<USHORT>(size_bytes);
+
+  NTSTATUS ret = CopyData(out_string->Buffer, start_ptr, out_string->Length);
+  if (!NT_SUCCESS(ret)) {
+    operator delete(out_string, NT_ALLOC);
+    return nullptr;
   }
-  return module_path;
+
+  out_string->Buffer[out_string->Length / sizeof(wchar_t)] = L'\0';
+  return out_string;
 }
 
 std::optional<bool> EqualUnicodeString(std::wstring_view left,
                                        std::wstring_view right) {
   UNICODE_STRING left_ustr;
   UNICODE_STRING right_ustr;
-  if (!ViewToUnicodeString(left, left_ustr) ||
-      !ViewToUnicodeString(right, right_ustr)) {
+  if (!base::win::ViewToUnicodeString(left, left_ustr) ||
+      !base::win::ViewToUnicodeString(right, right_ustr)) {
     return std::nullopt;
   }
 
diff --git a/sandbox/win/src/sandbox_nt_util.h b/sandbox/win/src/sandbox_nt_util.h
index 95cbe94..fea61a7 100644
--- a/sandbox/win/src/sandbox_nt_util.h
+++ b/sandbox/win/src/sandbox_nt_util.h
@@ -18,7 +18,6 @@
 #include <optional>
 #include <string_view>
 
-#include "base/compiler_specific.h"
 #include "base/containers/span.h"
 #include "base/memory/raw_ptr_exclusion.h"
 #include "sandbox/win/src/nt_internals.h"
@@ -106,23 +105,6 @@
   }
 };
 
-class ScopedUnicodeString {
- public:
-  ScopedUnicodeString();
-  ~ScopedUnicodeString();
-  // `str` should point into the `buffer` but it doesn't need to point to the
-  // start. This allows the class to manage the string view and the allocation
-  // separately.
-  ScopedUnicodeString(std::unique_ptr<uint8_t, NtAllocDeleter> buffer,
-                      std::wstring_view str);
-  std::wstring_view str() const LIFETIME_BOUND { return str_; }
-  bool valid() const { return !!buffer_; }
-
- private:
-  std::unique_ptr<uint8_t, NtAllocDeleter> buffer_;
-  std::wstring_view str_;
-};
-
 // Returns a pointer to the IPC shared memory.
 void* GetGlobalIPCMemory();
 
@@ -142,23 +124,36 @@
 // or write.
 bool ValidParameter(void* buffer, size_t size, RequiredAccess intent);
 
+// Copies data from a user buffer to our buffer. Returns the operation status.
+NTSTATUS CopyData(void* destination, const void* source, size_t bytes);
+
 // Initializes our ntdll level heap
 bool InitHeap();
 
 // Returns true if the provided handle refers to the current process.
 bool IsSameProcess(HANDLE process);
 
+enum MappedModuleFlags {
+  MODULE_IS_PE_IMAGE = 1,      // Module is an executable.
+  MODULE_HAS_ENTRY_POINT = 2,  // Execution entry point found.
+  MODULE_HAS_CODE = 4          // Non zero size of executable sections.
+};
+
 // Returns the name and characteristics for a given PE module. The return
-// value is the name as defined by the export table and a flag which indicates
-// if the module has any code.
+// value is the name as defined by the export table and the flags is any
+// combination of the MappedModuleFlags enumeration.
 //
-// ScopedUnicodeString name = GetImageInfoFromModule(HMODULE module, &has_code);
+// The returned buffer must be freed with a placement delete from the ntdll
+// level allocator:
+//
+// UNICODE_STRING* name = GetPEImageInfoFromModule(HMODULE module, &flags);
 // if (!name) {
 //   // probably not a valid dll
 //   return;
 // }
 // InsertYourLogicHere(name);
-ScopedUnicodeString GetImageInfoFromModule(HMODULE module, bool* has_code);
+// operator delete(name, NT_ALLOC);
+UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32_t* flags);
 
 // Returns the name and characteristics for a given PE module. The return
 // value is the name as defined by the export table.
@@ -168,15 +163,15 @@
 
 // Returns the full path and filename for a given dll.
 // May return nullptr if the provided address is not backed by a named section,
-// or if the current OS version doesn't support the call.
-ScopedUnicodeString GetBackingFilePath(PVOID address);
+// or if the current OS version doesn't support the call. The returned buffer
+// must be freed with a placement delete (see GetImageNameFromModule example).
+UNICODE_STRING* GetBackingFilePath(PVOID address);
 
-// Extract the last component of a path that contains the module name.
-// It will return an empty string if the path ends with the path separator or
-// there is no module name.
-// This is a view is into the `module_path` so it must be valid as long as
-// view remains valid.
-std::wstring_view ExtractModuleName(std::wstring_view module_path);
+// Returns the last component of a path that contains the module name.
+// It will return nullptr if the path ends with the path separator. The returned
+// buffer must be freed with a placement delete (see GetImageNameFromModule
+// example).
+UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path);
 
 // Returns true if the parameters correspond to a dll mapped as code.
 bool IsValidImageSection(HANDLE section,
@@ -185,7 +180,7 @@
                          PSIZE_T view_size);
 
 // Converts an ansi string to an UNICODE_STRING.
-ScopedUnicodeString AnsiToUnicode(const char* string);
+UNICODE_STRING* AnsiToUnicode(const char* string);
 
 // Use the RtlCompareUnicodeString API to compares two strings for equality. The
 // comparison always ignores case.
diff --git a/sandbox/win/src/sandbox_nt_util_unittest.cc b/sandbox/win/src/sandbox_nt_util_unittest.cc
index 7b0cb7c..eaae019 100644
--- a/sandbox/win/src/sandbox_nt_util_unittest.cc
+++ b/sandbox/win/src/sandbox_nt_util_unittest.cc
@@ -30,6 +30,8 @@
 namespace sandbox {
 namespace {
 
+using ScopedUnicodeString = std::unique_ptr<UNICODE_STRING, NtAllocDeleter>;
+
 TEST(SandboxNtUtil, IsSameProcessPseudoHandle) {
   HANDLE current_process_pseudo = GetCurrentProcess();
   EXPECT_TRUE(IsSameProcess(current_process_pseudo));
@@ -256,30 +258,45 @@
 
 TEST(SandboxNtUtil, ExtractModuleName) {
   {
-    std::wstring_view module_path = L"no-path-sep";
-    std::wstring_view result = ExtractModuleName(module_path);
-    EXPECT_FALSE(result.empty());
-    EXPECT_EQ(module_path, result);
+    UNICODE_STRING module_path = {};
+    ::RtlInitUnicodeString(&module_path, L"no-path-sep");
+    ScopedUnicodeString result(ExtractModuleName(&module_path));
+    EXPECT_TRUE(result);
+    EXPECT_EQ(result->Length, module_path.Length);
+    EXPECT_EQ(std::wstring(module_path.Buffer), std::wstring(result->Buffer));
   }
   {
-    std::wstring_view result =
-        ExtractModuleName(L"c:\\has a\\path\\module.dll");
-    EXPECT_FALSE(result.empty());
-    EXPECT_EQ(L"module.dll", result);
+    UNICODE_STRING module_path = {};
+    ::RtlInitUnicodeString(&module_path, L"c:\\has a\\path\\module.dll");
+    ScopedUnicodeString result(ExtractModuleName(&module_path));
+
+    EXPECT_TRUE(result);
+    EXPECT_EQ(result->Length, 10 * sizeof(wchar_t));
+    EXPECT_EQ(std::wstring(L"module.dll"), std::wstring(result->Buffer));
   }
   {
-    std::wstring_view result = ExtractModuleName(L"c:\\only a\\path\\");
-    EXPECT_TRUE(result.empty());
+    UNICODE_STRING module_path = {};
+    ::RtlInitUnicodeString(&module_path, L"c:\\only a\\path\\");
+    ScopedUnicodeString result(ExtractModuleName(&module_path));
+
+    EXPECT_FALSE(result);
   }
   {
-    std::wstring_view module_path = L"A";
-    std::wstring_view result = ExtractModuleName(module_path);
-    EXPECT_FALSE(result.empty());
-    EXPECT_EQ(module_path, result);
+    UNICODE_STRING module_path = {};
+    ::RtlInitUnicodeString(&module_path, L"A");
+    ScopedUnicodeString result(ExtractModuleName(&module_path));
+
+    EXPECT_TRUE(result);
+    EXPECT_EQ(result->Length, module_path.Length);
+    EXPECT_EQ(std::wstring(module_path.Buffer), std::wstring(result->Buffer));
   }
   {
-    std::wstring_view result = ExtractModuleName(L"");
-    EXPECT_TRUE(result.empty());
+    UNICODE_STRING module_path = {};
+    ::RtlInitUnicodeString(&module_path, L"");
+    ScopedUnicodeString result(ExtractModuleName(&module_path));
+
+    EXPECT_TRUE(result);
+    EXPECT_EQ(result->Length, 0);
   }
 }
 
diff --git a/sandbox/win/src/service_resolver.cc b/sandbox/win/src/service_resolver.cc
index 39e5817..638c11f 100644
--- a/sandbox/win/src/service_resolver.cc
+++ b/sandbox/win/src/service_resolver.cc
@@ -11,6 +11,16 @@
 
 namespace sandbox {
 
+NTSTATUS ServiceResolverThunk::ResolveInterceptor(
+    const void* interceptor_module,
+    const char* interceptor_name,
+    const void** address) {
+  // After all, we are using a locally mapped version of the exe, so the
+  // action is the same as for a target function.
+  return ResolveTarget(interceptor_module, interceptor_name,
+                       const_cast<void**>(address));
+}
+
 // In this case all the work is done from the parent, so resolve is
 // just a simple GetProcAddress.
 NTSTATUS ServiceResolverThunk::ResolveTarget(const void* module,
diff --git a/sandbox/win/src/service_resolver.h b/sandbox/win/src/service_resolver.h
index cdfc1903..dc74bd6e 100644
--- a/sandbox/win/src/service_resolver.h
+++ b/sandbox/win/src/service_resolver.h
@@ -31,12 +31,19 @@
 
   // Implementation of Resolver::Setup.
   NTSTATUS Setup(const void* target_module,
+                 const void* interceptor_module,
                  const char* target_name,
+                 const char* interceptor_name,
                  const void* interceptor_entry_point,
                  void* thunk_storage,
                  size_t storage_bytes,
                  size_t* storage_used) override;
 
+  // Implementation of Resolver::ResolveInterceptor.
+  NTSTATUS ResolveInterceptor(const void* module,
+                              const char* function_name,
+                              const void** address) override;
+
   // Implementation of Resolver::ResolveTarget.
   NTSTATUS ResolveTarget(const void* module,
                          const char* function_name,
diff --git a/sandbox/win/src/service_resolver_32.cc b/sandbox/win/src/service_resolver_32.cc
index 7f6bf39..bb558a1 100644
--- a/sandbox/win/src/service_resolver_32.cc
+++ b/sandbox/win/src/service_resolver_32.cc
@@ -148,13 +148,16 @@
 namespace sandbox {
 
 NTSTATUS ServiceResolverThunk::Setup(const void* target_module,
+                                     const void* interceptor_module,
                                      const char* target_name,
+                                     const char* interceptor_name,
                                      const void* interceptor_entry_point,
                                      void* thunk_storage,
                                      size_t storage_bytes,
                                      size_t* storage_used) {
-  NTSTATUS ret = Init(target_module, target_name, interceptor_entry_point,
-                      thunk_storage, storage_bytes);
+  NTSTATUS ret =
+      Init(target_module, interceptor_module, target_name, interceptor_name,
+           interceptor_entry_point, thunk_storage, storage_bytes);
   if (!NT_SUCCESS(ret))
     return ret;
 
diff --git a/sandbox/win/src/service_resolver_64.cc b/sandbox/win/src/service_resolver_64.cc
index 6558c23..8516e77 100644
--- a/sandbox/win/src/service_resolver_64.cc
+++ b/sandbox/win/src/service_resolver_64.cc
@@ -177,13 +177,16 @@
 namespace sandbox {
 
 NTSTATUS ServiceResolverThunk::Setup(const void* target_module,
+                                     const void* interceptor_module,
                                      const char* target_name,
+                                     const char* interceptor_name,
                                      const void* interceptor_entry_point,
                                      void* thunk_storage,
                                      size_t storage_bytes,
                                      size_t* storage_used) {
-  NTSTATUS ret = Init(target_module, target_name, interceptor_entry_point,
-                      thunk_storage, storage_bytes);
+  NTSTATUS ret =
+      Init(target_module, interceptor_module, target_name, interceptor_name,
+           interceptor_entry_point, thunk_storage, storage_bytes);
   if (!NT_SUCCESS(ret))
     return ret;
 
diff --git a/sandbox/win/src/service_resolver_unittest.cc b/sandbox/win/src/service_resolver_unittest.cc
index 4f6297f..78c3478 100644
--- a/sandbox/win/src/service_resolver_unittest.cc
+++ b/sandbox/win/src/service_resolver_unittest.cc
@@ -38,14 +38,16 @@
  protected:
   // Overrides Resolver::Init
   NTSTATUS Init(const void* target_module,
+                const void* interceptor_module,
                 const char* target_name,
+                const char* interceptor_name,
                 const void* interceptor_entry_point,
                 void* thunk_storage,
                 size_t storage_bytes) final {
     NTSTATUS ret = STATUS_SUCCESS;
-    ret = sandbox::ServiceResolverThunk::Init(target_module, target_name,
-                                              interceptor_entry_point,
-                                              thunk_storage, storage_bytes);
+    ret = sandbox::ServiceResolverThunk::Init(
+        target_module, interceptor_module, target_name, interceptor_name,
+        interceptor_entry_point, thunk_storage, storage_bytes);
     EXPECT_EQ(STATUS_SUCCESS, ret);
 
     this->target_ = fake_target_;
@@ -85,8 +87,9 @@
 
   // TODO(crbug.com/40285824): Eventually, `Setup` should just take a span<char>
   // instead of a pointer and a size.
-  NTSTATUS ret = resolver.Setup(ntdll_base, function, function_entry,
-                                thunk.data(), thunk_size, &used);
+  NTSTATUS ret =
+      resolver.Setup(ntdll_base, nullptr, function, nullptr, function_entry,
+                     thunk.data(), thunk_size, &used);
   if (NT_SUCCESS(ret)) {
     const BYTE kJump32 = 0xE9;
     EXPECT_EQ(thunk_size, used);
@@ -96,8 +99,8 @@
     if (relaxed) {
       // It's already patched, let's patch again, and simulate a direct patch.
       service[0] = kJump32;
-      ret = resolver.Setup(ntdll_base, function, function_entry, thunk.data(),
-                           thunk.size(), &used);
+      ret = resolver.Setup(ntdll_base, nullptr, function, nullptr,
+                           function_entry, thunk.data(), thunk.size(), &used);
       EXPECT_TRUE(resolver.VerifyJumpTargetForTesting(thunk.data()));
     }
   }
@@ -201,14 +204,14 @@
   NTSTATUS ret = STATUS_UNSUCCESSFUL;
 
   // First try patching without having allowed local patches.
-  ret = resolver.Setup(ntdll_base, kFunctionName, function_entry, thunk.data(),
-                       thunk_size, &used);
+  ret = resolver.Setup(ntdll_base, nullptr, kFunctionName, nullptr,
+                       function_entry, thunk.data(), thunk_size, &used);
   EXPECT_FALSE(NT_SUCCESS(ret));
 
   // Now allow local patches and check that things work.
   resolver.AllowLocalPatches();
-  ret = resolver.Setup(ntdll_base, kFunctionName, function_entry, thunk.data(),
-                       thunk_size, &used);
+  ret = resolver.Setup(ntdll_base, nullptr, kFunctionName, nullptr,
+                       function_entry, thunk.data(), thunk_size, &used);
   EXPECT_EQ(STATUS_SUCCESS, ret);
 }
 
diff --git a/sandbox/win/src/target_interceptions.cc b/sandbox/win/src/target_interceptions.cc
index 8d22fbb2..bcc5663 100644
--- a/sandbox/win/src/target_interceptions.cc
+++ b/sandbox/win/src/target_interceptions.cc
@@ -80,28 +80,34 @@
     if (!IsValidImageSection(section, base, offset, view_size))
       break;
 
-    bool has_code = false;
-    ScopedUnicodeString image_name =
-        GetImageInfoFromModule(reinterpret_cast<HMODULE>(*base), &has_code);
-    ScopedUnicodeString file_name = GetBackingFilePath(*base);
-    std::wstring_view module_name = image_name.str();
+    UINT image_flags;
+    UNICODE_STRING* module_name =
+        GetImageInfoFromModule(reinterpret_cast<HMODULE>(*base), &image_flags);
+    UNICODE_STRING* file_name = GetBackingFilePath(*base);
 
-    if (module_name.empty() && has_code) {
+    if ((!module_name) && (image_flags & MODULE_HAS_CODE)) {
       // If the module has no exports we retrieve the module name from the
       // full path of the mapped section.
-      module_name = ExtractModuleName(file_name.str());
+      module_name = ExtractModuleName(file_name);
     }
 
     InterceptionAgent* agent = InterceptionAgent::GetInterceptionAgent();
 
     if (agent) {
-      if (!agent->OnDllLoad(file_name.str(), module_name, *base)) {
+      if (!agent->OnDllLoad(file_name, module_name, *base)) {
         // Interception agent is demanding to un-map the module.
         GetNtExports()->UnmapViewOfSection(process, *base);
         *base = nullptr;
         ret = STATUS_UNSUCCESSFUL;
       }
     }
+
+    if (module_name)
+      operator delete(module_name, NT_ALLOC);
+
+    if (file_name)
+      operator delete(file_name, NT_ALLOC);
+
   } while (false);
 
   return ret;
diff --git a/services/device/public/cpp/device_feature_map.cc b/services/device/public/cpp/device_feature_map.cc
index 93008d7..3c7e3aa4 100644
--- a/services/device/public/cpp/device_feature_map.cc
+++ b/services/device/public/cpp/device_feature_map.cc
@@ -23,6 +23,7 @@
 // services/device/public/cpp/device_features.h or in other locations in the
 // code base.
 const base::Feature* const kFeaturesExposedToJava[] = {
+    &device::kWebAuthnAndroidSignal,
     &device::kWebAuthnImmediateGet,
     &device::kWebAuthnPasskeyUpgrade,
     &kGenericSensorExtraClasses,
diff --git a/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java b/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java
index 1e91e31..22b0716f 100644
--- a/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java
+++ b/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java
@@ -24,6 +24,7 @@
     public static final String GENERIC_SENSOR_EXTRA_CLASSES = "GenericSensorExtraClasses";
     public static final String BATTERY_STATUS_MANAGER_BROADCAST_RECEIVER_IN_BACKGROUND =
             "BatteryStatusManagerBroadcastReceiverInBackground";
+    public static final String WEBAUTHN_ANDROID_SIGNAL = "WebAuthenticationAndroidSignal";
     public static final String WEBAUTHN_PASSKEY_UPGRADE = "WebAuthenticationPasskeyUpgrade";
     public static final String WEBAUTHN_IMMEDIATE_GET = "WebAuthenticationImmediateGet";
 
diff --git a/services/network/cookie_settings.cc b/services/network/cookie_settings.cc
index bab1741..0ba2593 100644
--- a/services/network/cookie_settings.cc
+++ b/services/network/cookie_settings.cc
@@ -162,10 +162,9 @@
   content_settings_[type] =
       content_settings::HostIndexedContentSettings::Create(settings);
 
-  if (type == ContentSettingsType::COOKIES ||
-      type == ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL) {
-    // Cookies and the top-level origin trial for 3PCD use allow-by-default
-    // settings, so ensure their default is set appropriately.
+  if (type == ContentSettingsType::COOKIES) {
+    // Cookies use allow-by-default settings, so ensure the default is set
+    // appropriately.
     if (settings.empty() ||
         settings.back().primary_pattern != ContentSettingsPattern::Wildcard() ||
         settings.back().secondary_pattern !=
diff --git a/services/network/cookie_settings_unittest.cc b/services/network/cookie_settings_unittest.cc
index 370a129..cad260c 100644
--- a/services/network/cookie_settings_unittest.cc
+++ b/services/network/cookie_settings_unittest.cc
@@ -3672,487 +3672,6 @@
                          CookieSettingsTopLevelTpcdTrialTest,
                          testing::Bool());
 
-// The TOP_LEVEL_TPCD_ORIGIN_TRIAL content setting type is not registered on
-// iOS.
-
-#if !BUILDFLAG(IS_IOS)
-class CookieSettingsTopLevelTpcdOriginTrialTest
-    : public CookieSettingsTestBase,
-      public testing::
-          WithParamInterface</* net::features::kTopLevelTpcdOriginTrial:
-                              */
-                             bool> {
- public:
-  CookieSettingsTopLevelTpcdOriginTrialTest() {
-    std::vector<base::test::FeatureRef> enabled_features;
-    std::vector<base::test::FeatureRef> disabled_features;
-
-    if (IsTopLevel3pcdOriginTrialEligible()) {
-      enabled_features.push_back(net::features::kTopLevelTpcdOriginTrial);
-    } else {
-      disabled_features.push_back(net::features::kTopLevelTpcdOriginTrial);
-    }
-
-    enabled_features.push_back(net::features::kTpcdMetadataGrants);
-    feature_list_.InitWithFeatures(enabled_features, disabled_features);
-  }
-
-  bool IsTopLevel3pcdOriginTrialEligible() const { return GetParam(); }
-
-  net::CookieSettingOverrides GetCookieSettingOverrides() const {
-    net::CookieSettingOverrides overrides;
-    return overrides;
-  }
-
-  // The cookie access would be allowed if not for a
-  // `ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL` setting.
-  ContentSetting SettingWithTopLevel3pcdOriginTrialSetting() const {
-    return IsTopLevel3pcdOriginTrialEligible() ? CONTENT_SETTING_BLOCK
-                                               : CONTENT_SETTING_ALLOW;
-  }
-
-  // The storage access result would be allowed if not for a
-  // `ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL` setting.
-  net::cookie_util::StorageAccessResult
-  AllowedStorageAccessResultWithTopLevel3pcdOriginTrialSetting() const {
-    if (IsTopLevel3pcdOriginTrialEligible()) {
-      return net::cookie_util::StorageAccessResult::ACCESS_BLOCKED;
-    }
-    return net::cookie_util::StorageAccessResult::ACCESS_ALLOWED;
-  }
-
-  // The default scope for `ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL` is
-  // `WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE`, so this adds a
-  // `CONTENT_SETTING_BLOCK` setting with a pattern of that form to `settings`.
-  void AddSettingForTopLevelTpcdOriginTrial(CookieSettings& settings,
-                                            GURL top_level_url) {
-    settings.set_content_settings(
-        ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL,
-        {ContentSettingPatternSource(
-            ContentSettingsPattern::FromURLNoWildcard(top_level_url),
-            ContentSettingsPattern::Wildcard(),
-            base::Value(CONTENT_SETTING_BLOCK),
-            content_settings::ProviderType::kNone, false /* incognito */)});
-  }
-};
-
-// Verifies that `ContentSettingsType::TOP_LEVEL_TPCD_ORIGIN_TRIAL` is
-// allow-by-default and doesn't prevent cookie access.
-TEST_P(CookieSettingsTopLevelTpcdOriginTrialTest, AllowByDefault) {
-  CookieSettings settings;
-  settings.set_block_third_party_cookies(false);
-  settings.set_mitigations_enabled_for_3pcd(false);
-
-  EXPECT_EQ(settings.GetCookieSetting(GURL(kOtherURL), net::SiteForCookies(),
-                                      GURL(kURL), net::CookieSettingOverrides(),
-                                      nullptr),
-            CONTENT_SETTING_ALLOW);
-
-  net::CookieInclusionStatus status;
-  std::unique_ptr<net::CanonicalCookie> cookie =
-      MakeCanonicalSameSiteNoneCookie("name", kOtherURL);
-
-  EXPECT_TRUE(settings.IsCookieAccessible(
-      *cookie, GURL(kOtherURL), net::SiteForCookies(),
-      url::Origin::Create(GURL(kURL)), net::FirstPartySetMetadata(),
-      GetCookieSettingOverrides(), &status));
-}
-
-TEST_P(CookieSettingsTopLevelTpcdOriginTrialTest, IsCookieAccessible) {
-  CookieSettings settings;
-  net::CookieInclusionStatus status;
-  settings.set_block_third_party_cookies(false);
-  settings.set_mitigations_enabled_for_3pcd(false);
-
-  AddSettingForTopLevelTpcdOriginTrial(settings, GURL(kURL));
-
-  std::unique_ptr<net::CanonicalCookie> cookie =
-      MakeCanonicalSameSiteNoneCookie("name", kOtherURL);
-
-  EXPECT_NE(settings.IsCookieAccessible(
-                *cookie, GURL(kOtherURL), net::SiteForCookies(),
-                url::Origin::Create(GURL(kURL)), net::FirstPartySetMetadata(),
-                GetCookieSettingOverrides(), &status),
-            IsTopLevel3pcdOriginTrialEligible());
-  // When active, the `TOP_LEVEL_TPCD_ORIGIN_TRIAL` should be blocking
-  // third-party cookies, so the exemption reason should always be `kNone`.
-  EXPECT_EQ(status.exemption_reason(),
-            net::CookieInclusionStatus::ExemptionReason::kNone);
-  EXPECT_EQ(
-      status.HasExclusionReason(net::CookieInclusionStatus::ExclusionReason::
-                                    EXCLUDE_THIRD_PARTY_PHASEOUT),
-      IsTopLevel3pcdOriginTrialEligible());
-}
-
-TEST_P(CookieSettingsTopLevelTpcdOriginTrialTest,
-       OverridesHavePrecedenceOverMitigations) {
-  CookieSettings settings;
-  net::CookieInclusionStatus status;
-  settings.set_block_third_party_cookies(false);
-  settings.set_mitigations_enabled_for_3pcd(false);
-  GURL top_level_url = GURL(kURL);
-  GURL url = GURL(kOtherURL);
-
-  AddSettingForTopLevelTpcdOriginTrial(settings, top_level_url);
-  std::unique_ptr<net::CanonicalCookie> cookie =
-      MakeCanonicalSameSiteNoneCookie("name", kOtherURL);
-
-  // Third-party cookies are not blocked in general, but the OT blocks them when
-  // it is enabled. See that cookie gets blocked when there is a top level 3pcd
-  // origin trial.
-  EXPECT_NE(settings.IsCookieAccessible(*cookie, url, net::SiteForCookies(),
-                                        url::Origin::Create(top_level_url),
-                                        net::FirstPartySetMetadata(),
-                                        GetCookieSettingOverrides(), &status),
-            IsTopLevel3pcdOriginTrialEligible());
-
-  // Create a metadata grant to allow cookies for `url` under `top_level_url`.
-  network::tpcd::metadata::Manager manager;
-  manager.SetGrants({CreateSetting(url.GetHost(), top_level_url.GetHost(),
-                                   CONTENT_SETTING_ALLOW)});
-  settings.set_tpcd_metadata_manager(&manager);
-
-  EXPECT_TRUE(settings.IsCookieAccessible(
-      *cookie, url, net::SiteForCookies(), url::Origin::Create(top_level_url),
-      net::FirstPartySetMetadata(), GetCookieSettingOverrides(), &status));
-
-  // Ensure cookie access is always blocked when cookies are blocked from
-  // overrides.
-  EXPECT_FALSE(settings.IsCookieAccessible(
-      *cookie, url, net::SiteForCookies(), url::Origin::Create(top_level_url),
-      net::FirstPartySetMetadata(),
-      {net::CookieSettingOverride::kForceDisableThirdPartyCookies}, &status));
-}
-
-TEST_P(CookieSettingsTopLevelTpcdOriginTrialTest, UnblockedByMitigations) {
-  GURL top_level_url = GURL(kURL);
-  GURL url = GURL(kOtherURL);
-
-  CookieSettings settings;
-  base::HistogramTester histogram_tester;
-  settings.set_block_third_party_cookies(false);
-  settings.set_mitigations_enabled_for_3pcd(false);
-
-  histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0);
-
-  AddSettingForTopLevelTpcdOriginTrial(settings, top_level_url);
-
-  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
-                                      GetCookieSettingOverrides(), nullptr),
-            SettingWithTopLevel3pcdOriginTrialSetting());
-
-  // Create a metadata grant to allow cookies for `url` under `top_level_url`.
-  network::tpcd::metadata::Manager manager;
-  manager.SetGrants({CreateSetting(url.GetHost(), top_level_url.GetHost(),
-                                   CONTENT_SETTING_ALLOW)});
-  settings.set_tpcd_metadata_manager(&manager);
-
-  std::unique_ptr<net::CanonicalCookie> cookie =
-      MakeCanonicalSameSiteNoneCookie("name", kOtherURL);
-  net::CookieInclusionStatus status;
-
-  EXPECT_TRUE(settings.IsCookieAccessible(
-      *cookie, url, net::SiteForCookies(), url::Origin::Create(top_level_url),
-      net::FirstPartySetMetadata(), GetCookieSettingOverrides(), &status));
-  EXPECT_EQ(status.exemption_reason() ==
-                net::CookieInclusionStatus::ExemptionReason::k3PCDMetadata,
-            IsTopLevel3pcdOriginTrialEligible());
-
-  histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 2);
-  histogram_tester.ExpectBucketCount(
-      kAllowedRequestsHistogram,
-      net::cookie_util::StorageAccessResult::ACCESS_ALLOWED_3PCD_METADATA_GRANT,
-      IsTopLevel3pcdOriginTrialEligible() ? 1 : 0);
-
-  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
-                                      GetCookieSettingOverrides(), nullptr),
-            CONTENT_SETTING_ALLOW);
-}
-
-TEST_P(CookieSettingsTopLevelTpcdOriginTrialTest,
-       BypassDefaultAllowAllCookiesSetting) {
-  GURL top_level_url = GURL(kURL);
-  GURL url = GURL(kOtherURL);
-
-  CookieSettings settings;
-  settings.set_block_third_party_cookies(false);
-  settings.set_mitigations_enabled_for_3pcd(false);
-
-  // Set default cookie setting to allow ALL cookies.
-  settings.set_content_settings(
-      ContentSettingsType::COOKIES,
-      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});
-
-  // Add `TOP_LEVEL_TPCD_ORIGIN_TRIAL` setting for `top_level_url`.
-  AddSettingForTopLevelTpcdOriginTrial(settings, top_level_url);
-
-  base::HistogramTester histogram_tester;
-
-  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
-                                      GetCookieSettingOverrides(), nullptr),
-            SettingWithTopLevel3pcdOriginTrialSetting());
-
-  histogram_tester.ExpectUniqueSample(
-      kAllowedRequestsHistogram,
-      AllowedStorageAccessResultWithTopLevel3pcdOriginTrialSetting(), 1);
-}
-
-TEST_P(CookieSettingsTopLevelTpcdOriginTrialTest,
-       PreserveExplicitAllow3pcSetting) {
-  GURL top_level_url = GURL(kURL);
-  GURL url = GURL(kOtherURL);
-  base::HistogramTester histogram_tester;
-
-  CookieSettings settings;
-  settings.set_block_third_party_cookies(true);
-  settings.set_mitigations_enabled_for_3pcd(false);
-
-  // Explicitly allow third-party cookies for resources embedded under
-  // `top_level_url`.
-  settings.set_content_settings(
-      ContentSettingsType::COOKIES,
-      {CreateSetting("*", top_level_url.GetHost(), CONTENT_SETTING_ALLOW)});
-
-  // Add `TOP_LEVEL_TPCD_ORIGIN_TRIAL` setting for `top_level_url`.
-  AddSettingForTopLevelTpcdOriginTrial(settings, top_level_url);
-
-  histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0);
-
-  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
-                                      GetCookieSettingOverrides(), nullptr),
-            CONTENT_SETTING_ALLOW);
-
-  histogram_tester.ExpectUniqueSample(
-      kAllowedRequestsHistogram,
-      net::cookie_util::StorageAccessResult::ACCESS_ALLOWED, 1);
-
-  std::unique_ptr<net::CanonicalCookie> cookie =
-      MakeCanonicalSameSiteNoneCookie("name", kOtherURL);
-  net::CookieInclusionStatus status;
-
-  EXPECT_TRUE(settings.IsCookieAccessible(
-      *cookie, GURL(kOtherURL), net::SiteForCookies(),
-      url::Origin::Create(GURL(kURL)), net::FirstPartySetMetadata(),
-      GetCookieSettingOverrides(), &status));
-  EXPECT_EQ(status.exemption_reason(),
-            net::CookieInclusionStatus::ExemptionReason::kUserSetting);
-}
-
-TEST_P(CookieSettingsTopLevelTpcdOriginTrialTest,
-       PreserveExplicitBlock3pcSetting) {
-  GURL top_level_url = GURL(kURL);
-  GURL url = GURL(kOtherURL);
-  base::HistogramTester histogram_tester;
-
-  CookieSettings settings;
-  settings.set_block_third_party_cookies(false);
-  settings.set_mitigations_enabled_for_3pcd(false);
-
-  // Set default cookie setting to allow ALL cookies.
-  settings.set_content_settings(
-      ContentSettingsType::COOKIES,
-      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});
-
-  // Explicitly block third-party cookies for resources embedded under
-  // `top_level_url`.
-  settings.set_content_settings(
-      ContentSettingsType::COOKIES,
-      {CreateSetting("*", top_level_url.GetHost(), CONTENT_SETTING_BLOCK)});
-
-  // Check that 3P cookies are blocked.
-  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
-                                      GetCookieSettingOverrides(), nullptr),
-            CONTENT_SETTING_BLOCK);
-
-  // Add `TOP_LEVEL_TPCD_ORIGIN_TRIAL` setting for `top_level_url`.
-  AddSettingForTopLevelTpcdOriginTrial(settings, top_level_url);
-
-  // Create a metadata grant to allow cookies for `url` under
-  // `top_level_url`.
-  network::tpcd::metadata::Manager manager;
-  manager.SetGrants({CreateSetting(url.GetHost(), top_level_url.GetHost(),
-                                   CONTENT_SETTING_ALLOW)});
-  settings.set_tpcd_metadata_manager(&manager);
-
-  std::unique_ptr<net::CanonicalCookie> cookie =
-      MakeCanonicalSameSiteNoneCookie("name", kOtherURL);
-  net::CookieInclusionStatus status;
-
-  // Check that 3P cookies are still blocked for `url` under `top_level_url`.
-  EXPECT_FALSE(settings.IsCookieAccessible(
-      *cookie, url, net::SiteForCookies(), url::Origin::Create(top_level_url),
-      net::FirstPartySetMetadata(), GetCookieSettingOverrides(), &status));
-  EXPECT_EQ(status.exemption_reason(),
-            net::CookieInclusionStatus::ExemptionReason::kNone);
-
-  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
-                                      GetCookieSettingOverrides(), nullptr),
-            CONTENT_SETTING_BLOCK);
-}
-
-TEST_P(CookieSettingsTopLevelTpcdOriginTrialTest,
-       PreserveBlockAll3PCookiesSetting) {
-  GURL top_level_url = GURL(kURL);
-  GURL url = GURL(kOtherURL);
-
-  CookieSettings settings;
-  settings.set_block_third_party_cookies(true);
-  settings.set_mitigations_enabled_for_3pcd(false);
-
-  // Set default cookie setting to allow ALL cookies.
-  settings.set_content_settings(
-      ContentSettingsType::COOKIES,
-      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});
-
-  // Check that 3P cookies are blocked.
-  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
-                                      GetCookieSettingOverrides(), nullptr),
-            CONTENT_SETTING_BLOCK);
-
-  // Add `TOP_LEVEL_TPCD_ORIGIN_TRIAL` setting for `top_level_url`.
-  AddSettingForTopLevelTpcdOriginTrial(settings, top_level_url);
-
-  // Create a metadata grant to allow cookies for `url` under
-  // `top_level_url`.
-  network::tpcd::metadata::Manager manager;
-  manager.SetGrants({CreateSetting(url.GetHost(), top_level_url.GetHost(),
-                                   CONTENT_SETTING_ALLOW)});
-  settings.set_tpcd_metadata_manager(&manager);
-
-  std::unique_ptr<net::CanonicalCookie> cookie =
-      MakeCanonicalSameSiteNoneCookie("name", kOtherURL);
-  net::CookieInclusionStatus status;
-
-  // Check that 3P cookies are still blocked for `url` under `top_level_url`.
-  EXPECT_FALSE(settings.IsCookieAccessible(
-      *cookie, url, net::SiteForCookies(), url::Origin::Create(top_level_url),
-      net::FirstPartySetMetadata(), GetCookieSettingOverrides(), &status));
-  EXPECT_EQ(status.exemption_reason(),
-            net::CookieInclusionStatus::ExemptionReason::kNone);
-
-  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
-                                      GetCookieSettingOverrides(), nullptr),
-            CONTENT_SETTING_BLOCK);
-}
-
-TEST_P(CookieSettingsTopLevelTpcdOriginTrialTest,
-       PreserveBlockAllCookiesSetting) {
-  GURL top_level_url = GURL(kURL);
-  GURL url = GURL(kOtherURL);
-
-  CookieSettings settings;
-  settings.set_block_third_party_cookies(false);
-  settings.set_mitigations_enabled_for_3pcd(false);
-
-  // Set default cookie setting to block ALL cookies.
-  settings.set_content_settings(
-      ContentSettingsType::COOKIES,
-      {CreateSetting("*", "*", CONTENT_SETTING_BLOCK)});
-
-  // Check that 3P cookies are blocked.
-  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
-                                      GetCookieSettingOverrides(), nullptr),
-            CONTENT_SETTING_BLOCK);
-
-  // Add `TOP_LEVEL_TPCD_ORIGIN_TRIAL` setting for `top_level_url`.
-  AddSettingForTopLevelTpcdOriginTrial(settings, top_level_url);
-
-  // Create a metadata grant to allow cookies for `url` under
-  // `top_level_url`.
-  network::tpcd::metadata::Manager manager;
-  manager.SetGrants({CreateSetting(url.GetHost(), top_level_url.GetHost(),
-                                   CONTENT_SETTING_ALLOW)});
-  settings.set_tpcd_metadata_manager(&manager);
-
-  std::unique_ptr<net::CanonicalCookie> cookie =
-      MakeCanonicalSameSiteNoneCookie("name", kOtherURL);
-  net::CookieInclusionStatus status;
-
-  // Check that cookies are still blocked for `url` under `top_level_url`.
-  EXPECT_FALSE(settings.IsCookieAccessible(
-      *cookie, url, net::SiteForCookies(), url::Origin::Create(top_level_url),
-      net::FirstPartySetMetadata(), GetCookieSettingOverrides(), &status));
-  EXPECT_EQ(status.exemption_reason(),
-            net::CookieInclusionStatus::ExemptionReason::kNone);
-
-  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
-                                      GetCookieSettingOverrides(), nullptr),
-            CONTENT_SETTING_BLOCK);
-}
-
-TEST_P(CookieSettingsTopLevelTpcdOriginTrialTest,
-       AnnotateAndMoveUserBlockedCookies) {
-  CookieSettings settings;
-  net::CookieInclusionStatus status;
-  settings.set_block_third_party_cookies(false);
-  settings.set_mitigations_enabled_for_3pcd(false);
-
-  AddSettingForTopLevelTpcdOriginTrial(settings, GURL(kURL));
-
-  net::CookieAccessResultList maybe_included_cookies = {
-      {*MakeCanonicalSameSiteNoneCookie("third_party", kOtherURL), {}},
-  };
-  net::CookieAccessResultList excluded_cookies = {
-      {*MakeCanonicalSameSiteNoneCookie("excluded_other", kOtherURL),
-       // The `ExclusionReason` below is irrelevant, as long as there is one.
-       net::CookieAccessResult(
-           net::CookieInclusionStatus::MakeFromReasonsForTesting(
-               /*exclusions=*/{net::CookieInclusionStatus::ExclusionReason::
-                                   EXCLUDE_SECURE_ONLY}))}};
-
-  url::Origin origin = url::Origin::Create(GURL(kURL));
-
-  // Note that `url` does not match the `top_frame_origin`.
-  EXPECT_NE(settings.AnnotateAndMoveUserBlockedCookies(
-                GURL(kOtherURL), net::SiteForCookies(), &origin,
-                net::FirstPartySetMetadata(), GetCookieSettingOverrides(),
-                maybe_included_cookies, excluded_cookies),
-            IsTopLevel3pcdOriginTrialEligible());
-
-  if (IsTopLevel3pcdOriginTrialEligible()) {
-    EXPECT_THAT(maybe_included_cookies, IsEmpty());
-    EXPECT_THAT(excluded_cookies,
-                UnorderedElementsAre(
-                    MatchesCookieWithAccessResult(
-                        net::MatchesCookieWithName("excluded_other"),
-                        MatchesCookieAccessResult(
-                            net::HasExactlyExclusionReasonsForTesting(
-                                {net::CookieInclusionStatus::ExclusionReason::
-                                     EXCLUDE_SECURE_ONLY}),
-                            _, _, _)),
-                    MatchesCookieWithAccessResult(
-                        net::MatchesCookieWithName("third_party"),
-                        MatchesCookieAccessResult(
-                            net::HasExactlyExclusionReasonsForTesting(
-                                {net::CookieInclusionStatus::ExclusionReason::
-                                     EXCLUDE_THIRD_PARTY_PHASEOUT}),
-                            _, _, _))));
-  } else {
-    EXPECT_THAT(
-        maybe_included_cookies,
-        ElementsAre(MatchesCookieWithAccessResult(
-            net::MatchesCookieWithName("third_party"),
-            MatchesCookieAccessResult(
-                AllOf(net::IsInclude(),
-                      net::HasExactlyExemptionReason(
-                          net::CookieInclusionStatus::ExemptionReason::kNone)),
-                _, _, _))));
-    EXPECT_THAT(excluded_cookies,
-                UnorderedElementsAre(MatchesCookieWithAccessResult(
-                    net::MatchesCookieWithName("excluded_other"),
-                    MatchesCookieAccessResult(
-                        net::HasExactlyExclusionReasonsForTesting(
-                            {net::CookieInclusionStatus::ExclusionReason::
-                                 EXCLUDE_SECURE_ONLY}),
-                        _, _, _))));
-  }
-}
-
-INSTANTIATE_TEST_SUITE_P(/* no prefix */,
-                         CookieSettingsTopLevelTpcdOriginTrialTest,
-                         testing::Bool());
-#endif  // !BUILDFLAG(IS_IOS)
-
 struct CookieSettingsForceEnableOverrideTestData {
   std::vector<ContentSettingsType> settings_types;
   net::CookieSettingOverrides overrides;
diff --git a/services/network/cors/cors_url_loader_factory.cc b/services/network/cors/cors_url_loader_factory.cc
index c7a1cd8a4..d5a7d00 100644
--- a/services/network/cors/cors_url_loader_factory.cc
+++ b/services/network/cors/cors_url_loader_factory.cc
@@ -554,6 +554,11 @@
   return true;
 }
 
+bool CorsURLLoaderFactory::IsMultiNetworkCCTWorkFlow() const {
+  return context_->url_request_context()->bound_network() !=
+         net::handles::kInvalidNetworkHandle;
+}
+
 bool CorsURLLoaderFactory::IsCorsPreflighLoadOptionAllowed() const {
   // kURLLoadOptionAsCorsPreflight is set by CorsURLLoader itself, when
   // starting a request, if CORS preflight request is needed.
@@ -588,9 +593,7 @@
   // a valid network. So, given that this config is security critical, it's best
   // to "peek into implementation details" rather than granting this exception
   // to a bigger group.
-  return allow_external_preflights_for_testing_ ||
-         context_->url_request_context()->bound_network() !=
-             net::handles::kInvalidNetworkHandle;
+  return allow_external_preflights_for_testing_ || IsMultiNetworkCCTWorkFlow();
 }
 
 bool CorsURLLoaderFactory::IsValidRequest(const ResourceRequest& request,
@@ -617,8 +620,24 @@
   if (request.load_flags &
       (net::LOAD_CAN_USE_SHARED_DICTIONARY |
        net::LOAD_DISABLE_SHARED_DICTIONARY_AFTER_CROSS_ORIGIN_REDIRECT)) {
-    mojo::ReportBadMessage("CorsURLLoaderFactory: Internal load flag received");
-    return false;
+    // In the multi-network CCT workflow, when fetching the subresource, we
+    // create a nested CorsURLLoaderFactory to run the same request on a
+    // specific network. That causes this check to be invoked twice: the first
+    // invocation performs the authoritative validation, and later passes may
+    // add internal flags to the request. Re-validating here is unnecessary and
+    // can falsely reject otherwise valid requests (See
+    // CorsURLLoaderFactory::IsCorsPreflighLoadOptionAllowed for the rational).
+    // Note: Skipping this check is appropriate for subresource requests.
+    // For main page loads or navigation requests, ideally the check should
+    // still be performed as there is no nesting. However, since this is only
+    // a sanity check, skipping it here is safe.
+    // TODO(crbug.com/449098586): refactor to get rid of nested
+    // CorsURLLoaderFactory to make the design clearer.
+    if (!IsMultiNetworkCCTWorkFlow()) {
+      mojo::ReportBadMessage(
+          "CorsURLLoaderFactory: Internal load flag received");
+      return false;
+    }
   }
 
   // Check if this is an untrusted factory being provided parameters that should
diff --git a/services/network/cors/cors_url_loader_factory.h b/services/network/cors/cors_url_loader_factory.h
index 2baf57ef..a67010c3 100644
--- a/services/network/cors/cors_url_loader_factory.h
+++ b/services/network/cors/cors_url_loader_factory.h
@@ -160,6 +160,9 @@
   mojo::PendingRemote<mojom::DevToolsObserver> GetDevToolsObserver(
       ResourceRequest& resource_request) const;
 
+  // Returns whether this factory is used in the multi-network CCT workflow.
+  bool IsMultiNetworkCCTWorkFlow() const;
+
   template <class T>
   void OnLoaderCreated(
       std::unique_ptr<T> loader,
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index a021618..c99cb04 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -3157,9 +3157,17 @@
   std::unique_ptr<net::CookieCryptoDelegate> crypto_delegate = nullptr;
 
   if (params_->enable_encrypted_cookies) {
-    CHECK(params_->cookie_encryption_provider);
-    crypto_delegate = std::make_unique<CookieOSCryptAsyncDelegate>(
-        std::move(params_->cookie_encryption_provider));
+    if (params_->cookie_encryption_provider) {
+      crypto_delegate = std::make_unique<CookieOSCryptAsyncDelegate>(
+          std::move(params_->cookie_encryption_provider));
+    } else {
+#if !BUILDFLAG(IS_ANDROID)
+      // A cookie crypto delegate should not be created on Android to
+      // match the behavior of cookie_config::GetCookieCryptoDelegate().
+      // See https://crbug.com/449652881
+      NOTREACHED();
+#endif
+    }
   }
 
 #if BUILDFLAG(IS_WIN)
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc
index 7c45c6c..da3acca 100644
--- a/services/network/public/cpp/features.cc
+++ b/services/network/public/cpp/features.cc
@@ -306,6 +306,14 @@
                    &kRendererSideContentDecoding,
                    /*name=*/"RendererSideContentDecodingPipeSize",
                    /*default_value=*/0);
+// Enables renderer-side content decoding for navigations. This is a sub-feature
+// of `kRendererSideContentDecoding` and has no effect if that feature is
+// disabled.
+BASE_FEATURE_PARAM(bool,
+                   kRendererSideContentDecodingForNavigation,
+                   &kRendererSideContentDecoding,
+                   /*name=*/"RendererSideContentDecodingForNavigation",
+                   /*default_value=*/true);
 // For testing purposes only. If set to true, the creation of the Mojo data pipe
 // for the RendererSideContentDecoding feature will be forced to fail,
 // simulating an insufficient resources error (net::ERR_INSUFFICIENT_RESOURCES).
diff --git a/services/network/public/cpp/features.h b/services/network/public/cpp/features.h
index 4ed908d..88dc5644 100644
--- a/services/network/public/cpp/features.h
+++ b/services/network/public/cpp/features.h
@@ -130,6 +130,8 @@
 COMPONENT_EXPORT(NETWORK_CPP_FLAGS_AND_SWITCHES)
 BASE_DECLARE_FEATURE_PARAM(int, kRendererSideContentDecodingPipeSize);
 COMPONENT_EXPORT(NETWORK_CPP_FLAGS_AND_SWITCHES)
+BASE_DECLARE_FEATURE_PARAM(bool, kRendererSideContentDecodingForNavigation);
+COMPONENT_EXPORT(NETWORK_CPP_FLAGS_AND_SWITCHES)
 BASE_DECLARE_FEATURE_PARAM(
     bool,
     kRendererSideContentDecodingForceMojoFailureForTesting);
diff --git a/services/network/shared_dictionary/shared_dictionary_storage.cc b/services/network/shared_dictionary/shared_dictionary_storage.cc
index 7c696b6..850b765 100644
--- a/services/network/shared_dictionary/shared_dictionary_storage.cc
+++ b/services/network/shared_dictionary/shared_dictionary_storage.cc
@@ -260,7 +260,7 @@
   }
 
   auto matcher_create_result =
-      url_pattern::SimpleUrlPatternMatcher::Create(info->match, url);
+      url_pattern::SimpleUrlPatternMatcher::Create(info->match, &url);
   if (!matcher_create_result.has_value()) {
     return base::unexpected(
         mojom::SharedDictionaryError::kWriteErrorInvalidMatchField);
diff --git a/services/network/shared_dictionary/shared_dictionary_storage_on_disk.cc b/services/network/shared_dictionary/shared_dictionary_storage_on_disk.cc
index bf8360c..42c0c69 100644
--- a/services/network/shared_dictionary/shared_dictionary_storage_on_disk.cc
+++ b/services/network/shared_dictionary/shared_dictionary_storage_on_disk.cc
@@ -267,7 +267,7 @@
     const std::string match = info.match();
     std::unique_ptr<url_pattern::SimpleUrlPatternMatcher> matcher;
     auto matcher_create_result =
-        url_pattern::SimpleUrlPatternMatcher::Create(match, info.url());
+        url_pattern::SimpleUrlPatternMatcher::Create(match, &info.url());
     if (!matcher_create_result.has_value()) {
       continue;
     }
diff --git a/services/network/shared_resource_checker.cc b/services/network/shared_resource_checker.cc
index b47fc9b..73d684f 100644
--- a/services/network/shared_resource_checker.cc
+++ b/services/network/shared_resource_checker.cc
@@ -34,7 +34,7 @@
   ~PatternEntry() = default;
   PatternEntry(const std::string& pattern, const GURL& base_url) {
     auto pattern_create_result =
-        url_pattern::SimpleUrlPatternMatcher::Create(pattern, base_url);
+        url_pattern::SimpleUrlPatternMatcher::Create(pattern, &base_url);
     if (pattern_create_result.has_value()) {
       url_pattern_ = std::move(pattern_create_result.value());
     }
diff --git a/services/network/throttling/throttling_controller.cc b/services/network/throttling/throttling_controller.cc
index 0187fb3..9695650f3 100644
--- a/services/network/throttling/throttling_controller.cc
+++ b/services/network/throttling/throttling_controller.cc
@@ -129,11 +129,15 @@
   // same time. If this grows too large me need to build maps from the
   // conditions here.
   for (auto& [pattern, network_conditions] : conditions) {
-    auto pattern_matcher = url_pattern::SimpleUrlPatternMatcher::Create(
-        pattern, GURL("https://*"));
-    if (!pattern.empty() &&
-        (!pattern_matcher.has_value() || !*pattern_matcher)) {
-      continue;
+    std::unique_ptr<url_pattern::SimpleUrlPatternMatcher> pattern_matcher;
+    if (!pattern.empty()) {
+      auto maybe_pattern_matcher = url_pattern::SimpleUrlPatternMatcher::Create(
+          pattern, /*base_url=*/nullptr);
+      if (!maybe_pattern_matcher.has_value()) {
+        continue;
+      }
+      pattern_matcher = std::move(*maybe_pattern_matcher);
+      CHECK(pattern_matcher);
     }
 
     if (auto old_entry = FindConditions(old_matchers.begin(),
@@ -144,16 +148,16 @@
 
       matchers_.back().patterns.clear();
       matchers_.back().patterns.emplace_back(std::move(pattern),
-                                             std::move(*pattern_matcher));
+                                             std::move(pattern_matcher));
     } else if (auto new_entry = FindConditions(
                    matchers_.begin(), matchers_.end(), network_conditions);
                new_entry != matchers_.end()) {
       new_entry->patterns.emplace_back(std::move(pattern),
-                                       std::move(*pattern_matcher));
+                                       std::move(pattern_matcher));
     } else {
       matchers_.emplace_back(std::move(network_conditions));
       matchers_.back().patterns.emplace_back(std::move(pattern),
-                                             std::move(*pattern_matcher));
+                                             std::move(pattern_matcher));
     }
   }
 }
diff --git a/services/network/throttling/throttling_controller_unittest.cc b/services/network/throttling/throttling_controller_unittest.cc
index 48ae373..ba72248e 100644
--- a/services/network/throttling/throttling_controller_unittest.cc
+++ b/services/network/throttling/throttling_controller_unittest.cc
@@ -375,6 +375,7 @@
   // but in some cases fails to parse:
   helper.SetNetworkState({
       {"ht tp://", NetworkConditions{false}},
+      {"*.css", NetworkConditions{false}},
   });
   EXPECT_EQ(helper.GetThrottlingProfile()->matcher_count(), 0u);
 }
diff --git a/services/viz/privileged/mojom/gl/gpu_service.mojom b/services/viz/privileged/mojom/gl/gpu_service.mojom
index b8eb87f..6301e5f7 100644
--- a/services/viz/privileged/mojom/gl/gpu_service.mojom
+++ b/services/viz/privileged/mojom/gl/gpu_service.mojom
@@ -56,10 +56,13 @@
   // client. The GPU service responds with an IPC handle. |gpu_info| and
   // |gpu_feature_info| are returned so that if this method is called
   // synchronously they are available (since the PostTask for
-  // GpuHost::DidInitialize might not be dispatched yet.
+  // GpuHost::DidInitialize might not be dispatched yet).
   [Sync, NoInterrupt]
   EstablishGpuChannel(
-      int32 client_id, uint64 client_tracing_id, bool is_gpu_host)
+      int32 client_id,
+      uint64 client_tracing_id,
+      bool is_gpu_host,
+      bool enable_extra_handles_validation)
       => (handle<message_pipe>? channel_handle,
           gpu.mojom.GpuInfo gpu_info,
           gpu.mojom.GpuFeatureInfo gpu_feature_info,
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index ddfeeba..8e7a92c 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -1738,8 +1738,7 @@
         "merge": {
           "args": [
             "--lightweight",
-            "--skip-perf",
-            "--upload-skia-json"
+            "--skip-perf"
           ],
           "script": "//tools/perf/process_perf_results.py"
         },
@@ -1774,6 +1773,7 @@
       }
     ]
   },
+  "mac-m4-mini-processor-perf": {},
   "win-10-perf": {
     "isolated_scripts": [
       {
diff --git a/testing/buildbot/chromium.perf.pinpoint.json b/testing/buildbot/chromium.perf.pinpoint.json
index 2675e25..8bfa3d4f7 100644
--- a/testing/buildbot/chromium.perf.pinpoint.json
+++ b/testing/buildbot/chromium.perf.pinpoint.json
@@ -1472,8 +1472,7 @@
         "merge": {
           "args": [
             "--lightweight",
-            "--skip-perf",
-            "--upload-skia-json"
+            "--skip-perf"
           ],
           "script": "//tools/perf/process_perf_results.py"
         },
@@ -1508,6 +1507,7 @@
       }
     ]
   },
+  "mac-m4-mini-processor-perf": {},
   "win-10-perf": {
     "isolated_scripts": [
       {
diff --git a/testing/perf/cbb_ref_info/edge/dev/windows.json b/testing/perf/cbb_ref_info/edge/dev/windows.json
index 477d013..00bb32bf 100644
--- a/testing/perf/cbb_ref_info/edge/dev/windows.json
+++ b/testing/perf/cbb_ref_info/edge/dev/windows.json
@@ -2,5 +2,5 @@
   "browser": "edge",
   "channel": "dev",
   "platform": "windows",
-  "version": "141.0.3537.9"
-}
+  "version": "142.0.3581.0"
+}
\ No newline at end of file
diff --git a/testing/perf/cbb_ref_info/edge/stable/windows.json b/testing/perf/cbb_ref_info/edge/stable/windows.json
index 32c8b6c..cfa1128 100644
--- a/testing/perf/cbb_ref_info/edge/stable/windows.json
+++ b/testing/perf/cbb_ref_info/edge/stable/windows.json
@@ -2,5 +2,5 @@
   "browser": "edge",
   "channel": "stable",
   "platform": "windows",
-  "version": "140.0.3485.54"
-}
+  "version": "141.0.3537.57"
+}
\ No newline at end of file
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index b3c5b4e..d21a14a 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -47,11 +47,15 @@
 import pathlib
 import shlex
 import shutil
+import subprocess
 import sys
 import time
 import tempfile
 import traceback
 
+if sys.platform == 'darwin':
+  import plistlib
+
 # vpython-provided modules.
 # pylint: disable=import-error
 import six
@@ -102,6 +106,12 @@
 PAGE_SETS_DATA = CHROMIUM_SRC_DIR / 'tools/perf/page_sets/data'
 PERF_TOOLS = ['benchmarks', 'executables', 'crossbench']
 
+# Constants for CBB support.
+# CBB uses a pseudo benchmark that retrieves the versions of alternative
+# browsers (Edge on Windows, or Safari on Mac) installed on the device.
+CBB_BROWSER_VERSIONS_BENCHMARK = 'browser_versions'
+CBB_BROWSER_VERSIONS_FILENAME = 'browser_versions.json'
+
 # See https://crbug.com/923564.
 # We want to switch over to using histograms for everything, but converting from
 # the format output by gtest perf tests to histograms has introduced several
@@ -1209,6 +1219,81 @@
   os.chdir(candidates[0])
 
 
+def get_browser_versions(isolated_out_dir):
+  """Detect versions of alternative browsers installed on the device.
+
+  Detect which version of Edge (and Safari in the future) is currently
+  installed. The result is saved in a file named CBB_BROWSER_VERSIONS_FILENAME
+  in the isolated output directory.
+  """
+  results = {}
+  if IsWindows():
+    channels = {
+        'stable':
+        'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe',
+        'dev':
+        'C:/Program Files (x86)/Microsoft/Edge Dev/Application/msedge.exe',
+    }
+    for channel, path in channels.items():
+      cmd = [
+          'powershell', '-command',
+          f"(Get-Item '{path}').VersionInfo.ProductVersion"
+      ]
+      results[channel] = subprocess.run(cmd,
+                                        capture_output=True,
+                                        encoding='utf8',
+                                        check=True).stdout.strip()
+  elif sys.platform == 'darwin':
+    channels = {
+        'stable': {
+            'driver': '/usr/bin/safaridriver',
+            'prefix': 'Included with Safari ',
+        },
+        # pylint: disable=line-too-long
+        'technology-preview': {
+            'plist': '/Applications/Safari Technology Preview.app/Contents/Info.plist',
+            'driver': '/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver',
+            'prefix': 'Included with Safari Technology Preview ',
+        },
+        # pylint: enable=line-too-long
+    }
+    for channel, info in channels.items():
+      driver_version = subprocess.run([info['driver'], '--version'],
+                                      capture_output=True,
+                                      encoding='utf8',
+                                      check=True).stdout.strip()
+      prefix = info['prefix']
+      if not driver_version.startswith(prefix):
+        # pylint: disable=line-too-long
+        print(f'Missing expected prefix from Safari {channel} output: {driver_version}')
+        # pylint: enable=line-too-long
+        return 1
+      driver_version = driver_version[len(prefix):]
+      # For Safari stable, the version reported by safaridriver is complete and
+      # can be used as is. For Safari Technology Preview, however, the version
+      # reported by safaridriver is missing the main version (such as '26.0'),
+      # and we need to retrieve it from the app's plist file.
+      if plist_path := info.get('plist'):
+        plist = plistlib.loads(pathlib.Path(plist_path).read_bytes())
+        version = plist.get('CFBundleShortVersionString')
+        if not version:
+          print(f'Missing version info for Safari {channel}')
+          return 1
+        results[channel] = f'{version} {driver_version}'
+      else:
+        results[channel] = driver_version
+  else:
+    print('Only Windows OS and MacOS are supported')
+    return 1
+
+  with open(os.path.join(isolated_out_dir, CBB_BROWSER_VERSIONS_FILENAME),
+            'w') as f:
+    json.dump(results, f)
+    f.write('\n')
+
+  return 0
+
+
 def main(sys_args):
   sys.stdout.reconfigure(line_buffering=True)
   _set_cwd()
@@ -1272,6 +1357,8 @@
         options.xvfb,
         results_label=options.results_label)
     test_results_files.append(output_paths.test_results)
+  elif options.benchmarks == CBB_BROWSER_VERSIONS_BENCHMARK:
+    overall_return_code = get_browser_versions(isolated_out_dir)
   elif options.benchmarks:
     benchmarks = options.benchmarks.split(',')
     for benchmark in benchmarks:
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 93107af..78da6ba 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -8488,24 +8488,6 @@
             ]
         }
     ],
-    "DesktopNewTopAreaLayout": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "DesktopNewTopAreaLayout"
-                    ]
-                }
-            ]
-        }
-    ],
     "DesktopNtpDriveCache": [
         {
             "platforms": [
@@ -9695,6 +9677,29 @@
             ]
         }
     ],
+    "EnableBestEffortTaskInhibitingPolicy": [
+        {
+            "platforms": [
+                "android",
+                "ios",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "enable_best_effort_task_inhibiting_minimum_allowed_time_per_period": "1s",
+                        "enable_best_effort_task_inhibiting_period": "2s"
+                    },
+                    "enable_features": [
+                        "EnableBestEffortTaskInhibitingPolicy"
+                    ]
+                }
+            ]
+        }
+    ],
     "EnableChromeFatalCrashEventsObserver": [
         {
             "platforms": [
@@ -10750,24 +10755,6 @@
             ]
         }
     ],
-    "FasterBackgroundFreezing": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "DelayForBackgroundTabFreezingMills": "60000"
-                    },
-                    "enable_features": [
-                        "stop-in-background"
-                    ]
-                }
-            ]
-        }
-    ],
     "FedCmSegmentationPlatform": [
         {
             "platforms": [
@@ -11499,22 +11486,22 @@
             ],
             "experiments": [
                 {
-                    "name": "Arm1_HighlightOnly",
+                    "name": "Arm2_LabelOnly",
                     "params": {
                         "glic-entrypoint-variations-alt-icon": "false",
-                        "glic-entrypoint-variations-highlight-nudge": "true",
-                        "glic-entrypoint-variations-show-label": "false"
+                        "glic-entrypoint-variations-highlight-nudge": "false",
+                        "glic-entrypoint-variations-show-label": "true"
                     },
                     "enable_features": [
                         "GlicEntrypointVariations"
                     ]
                 },
                 {
-                    "name": "Arm2_LabelOnly",
+                    "name": "Arm1_HighlightOnly",
                     "params": {
                         "glic-entrypoint-variations-alt-icon": "false",
-                        "glic-entrypoint-variations-highlight-nudge": "false",
-                        "glic-entrypoint-variations-show-label": "true"
+                        "glic-entrypoint-variations-highlight-nudge": "true",
+                        "glic-entrypoint-variations-show-label": "false"
                     },
                     "enable_features": [
                         "GlicEntrypointVariations"
@@ -15853,8 +15840,7 @@
                 {
                     "name": "Enabled",
                     "enable_features": [
-                        "DidObserveNewFeatureUsageImprovement",
-                        "DidObserveSubresourceLoadImprovement"
+                        "MetricsRenderFrameObserverImprovement"
                     ]
                 }
             ]
@@ -17330,6 +17316,26 @@
             ]
         }
     ],
+    "OriginMatcherNewCopyAssignment": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "OriginMatcherNewCopyAssignment"
+                    ]
+                }
+            ]
+        }
+    ],
     "OutOfProcessPrintDriversPrint": [
         {
             "platforms": [
@@ -24400,16 +24406,31 @@
             ]
         }
     ],
-    "SupportMultipleServerRequestsForPixPayments": [
+    "SubframeProcessShutdownDelay": [
         {
             "platforms": [
-                "android"
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
             ],
             "experiments": [
                 {
-                    "name": "Enabled",
+                    "name": "EnabledDelay10s",
+                    "params": {
+                        "delay_seconds": "10"
+                    },
                     "enable_features": [
-                        "SupportMultipleServerRequestsForPixPayments"
+                        "SubframeProcessShutdownDelay"
+                    ]
+                },
+                {
+                    "name": "EnabledDelay40s",
+                    "params": {
+                        "delay_seconds": "40"
+                    },
+                    "enable_features": [
+                        "SubframeProcessShutdownDelay"
                     ]
                 }
             ]
@@ -25856,6 +25877,25 @@
             ]
         }
     ],
+    "UsePersistentCacheForCodeCache": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "windows",
+                "mac"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "UsePersistentCacheForCodeCache"
+                    ]
+                }
+            ]
+        }
+    ],
     "UseSCContentSharingPicker": [
         {
             "platforms": [
@@ -26533,25 +26573,6 @@
             ]
         }
     ],
-    "VerifyQWACsRollout": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "VerifyQWACs"
-                    ]
-                }
-            ]
-        }
-    ],
     "VideoConferenceDLCUIRollout": [
         {
             "platforms": [
@@ -26673,12 +26694,25 @@
                     "name": "EnabledFSM",
                     "enable_features": [
                         "VizDirectCompositorThreadIpcFrameSinkManager"
+                    ],
+                    "disable_features": [
+                        "VizDirectCompositorThreadIpcNonRoot"
                     ]
                 },
                 {
                     "name": "EnabledNonRoot",
                     "enable_features": [
                         "VizDirectCompositorThreadIpcNonRoot"
+                    ],
+                    "disable_features": [
+                        "VizDirectCompositorThreadIpcFrameSinkManager"
+                    ]
+                },
+                {
+                    "name": "Control",
+                    "disable_features": [
+                        "VizDirectCompositorThreadIpcFrameSinkManager",
+                        "VizDirectCompositorThreadIpcNonRoot"
                     ]
                 }
             ]
@@ -26694,12 +26728,25 @@
                     "name": "EnabledFSM",
                     "enable_features": [
                         "VizDirectCompositorThreadIpcFrameSinkManager"
+                    ],
+                    "disable_features": [
+                        "VizDirectCompositorThreadIpcNonRoot"
                     ]
                 },
                 {
                     "name": "EnabledNonRoot",
                     "enable_features": [
                         "VizDirectCompositorThreadIpcNonRoot"
+                    ],
+                    "disable_features": [
+                        "VizDirectCompositorThreadIpcFrameSinkManager"
+                    ]
+                },
+                {
+                    "name": "Control",
+                    "disable_features": [
+                        "VizDirectCompositorThreadIpcFrameSinkManager",
+                        "VizDirectCompositorThreadIpcNonRoot"
                     ]
                 }
             ]
diff --git a/third_party/abseil-cpp/CMake/AbseilDll.cmake b/third_party/abseil-cpp/CMake/AbseilDll.cmake
index 8898f37..64f9436 100644
--- a/third_party/abseil-cpp/CMake/AbseilDll.cmake
+++ b/third_party/abseil-cpp/CMake/AbseilDll.cmake
@@ -19,7 +19,6 @@
   "base/internal/endian.h"
   "base/internal/errno_saver.h"
   "base/internal/hide_ptr.h"
-  "base/internal/identity.h"
   "base/internal/iterator_traits.h"
   "base/internal/low_level_alloc.cc"
   "base/internal/low_level_alloc.h"
diff --git a/third_party/abseil-cpp/README.chromium b/third_party/abseil-cpp/README.chromium
index 88d336f..bdb865e 100644
--- a/third_party/abseil-cpp/README.chromium
+++ b/third_party/abseil-cpp/README.chromium
@@ -4,7 +4,7 @@
 License: Apache-2.0
 License File: LICENSE
 Version: N/A
-Revision: aef36c342b796a9fc932ea06ecbe3ce26bbd05a6
+Revision: 6315c20271bc7f192584cc515f3a6dd8337564ec
 Update Mechanism: Manual
 Security Critical: yes
 Shipped: yes
diff --git a/third_party/abseil-cpp/absl/base/BUILD.bazel b/third_party/abseil-cpp/absl/base/BUILD.bazel
index 08ae162..3770b39 100644
--- a/third_party/abseil-cpp/absl/base/BUILD.bazel
+++ b/third_party/abseil-cpp/absl/base/BUILD.bazel
@@ -88,10 +88,7 @@
     hdrs = ["nullability.h"],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    deps = [
-        ":config",
-        ":core_headers",
-    ],
+    deps = [":config"],
 )
 
 cc_library(
@@ -241,7 +238,6 @@
     name = "base_internal",
     hdrs = [
         "internal/hide_ptr.h",
-        "internal/identity.h",
         "internal/scheduling_mode.h",
     ],
     copts = ABSL_DEFAULT_COPTS,
@@ -636,7 +632,6 @@
     name = "nullability_test",
     srcs = ["nullability_test.cc"],
     deps = [
-        ":core_headers",
         ":nullability",
         "@googletest//:gtest",
         "@googletest//:gtest_main",
diff --git a/third_party/abseil-cpp/absl/base/BUILD.gn b/third_party/abseil-cpp/absl/base/BUILD.gn
index 1bcf3bc..5164bc7 100644
--- a/third_party/abseil-cpp/absl/base/BUILD.gn
+++ b/third_party/abseil-cpp/absl/base/BUILD.gn
@@ -40,7 +40,6 @@
   public = [ "nullability.h" ]
   deps = [
     ":config",
-    ":core_headers",
   ]
 }
 
@@ -152,7 +151,6 @@
 absl_source_set("base_internal") {
   public = [
     "internal/hide_ptr.h",
-    "internal/identity.h",
     "internal/scheduling_mode.h",
   ]
   deps = [
@@ -392,7 +390,6 @@
 absl_test("nullability_test") {
   sources = [ "nullability_test.cc" ]
   deps = [
-    ":core_headers",
     ":nullability",
   ]
 }
diff --git a/third_party/abseil-cpp/absl/base/CMakeLists.txt b/third_party/abseil-cpp/absl/base/CMakeLists.txt
index b2b7997..daffdb4 100644
--- a/third_party/abseil-cpp/absl/base/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/base/CMakeLists.txt
@@ -74,7 +74,6 @@
     "nullability.h"
   DEPS
     absl::config
-    absl::core_headers
   COPTS
     ${ABSL_DEFAULT_COPTS}
 )
@@ -87,7 +86,6 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
-    absl::core_headers
     absl::nullability
     GTest::gtest_main
 )
@@ -238,7 +236,6 @@
     base_internal
   HDRS
     "internal/hide_ptr.h"
-    "internal/identity.h"
     "internal/scheduling_mode.h"
   COPTS
     ${ABSL_DEFAULT_COPTS}
diff --git a/third_party/abseil-cpp/absl/base/casts.h b/third_party/abseil-cpp/absl/base/casts.h
index e0b11bb..fc4ec6a7 100644
--- a/third_party/abseil-cpp/absl/base/casts.h
+++ b/third_party/abseil-cpp/absl/base/casts.h
@@ -33,7 +33,6 @@
 #include <bit>  // For std::bit_cast.
 #endif  // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
 
-#include "absl/base/internal/identity.h"
 #include "absl/base/macros.h"
 #include "absl/meta/type_traits.h"
 
@@ -90,7 +89,7 @@
 //
 // Such implicit cast chaining may be useful within template logic.
 template <typename To>
-constexpr To implicit_cast(typename absl::internal::type_identity_t<To> to) {
+constexpr To implicit_cast(typename absl::type_identity_t<To> to) {
   return to;
 }
 
diff --git a/third_party/abseil-cpp/absl/base/internal/identity.h b/third_party/abseil-cpp/absl/base/internal/identity.h
deleted file mode 100644
index 365207b..0000000
--- a/third_party/abseil-cpp/absl/base/internal/identity.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef ABSL_BASE_INTERNAL_IDENTITY_H_
-#define ABSL_BASE_INTERNAL_IDENTITY_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace internal {
-
-// This is a back-fill of C++20's `std::type_identity`.
-template <typename T>
-struct type_identity {
-  typedef T type;
-};
-
-// This is a back-fill of C++20's `std::type_identity_t`.
-template <typename T>
-using type_identity_t = typename type_identity<T>::type;
-
-}  // namespace internal
-ABSL_NAMESPACE_END
-}  // namespace absl
-
-#endif  // ABSL_BASE_INTERNAL_IDENTITY_H_
diff --git a/third_party/abseil-cpp/absl/container/internal/hashtable_control_bytes.h b/third_party/abseil-cpp/absl/container/internal/hashtable_control_bytes.h
index 0501e48..f0fd354 100644
--- a/third_party/abseil-cpp/absl/container/internal/hashtable_control_bytes.h
+++ b/third_party/abseil-cpp/absl/container/internal/hashtable_control_bytes.h
@@ -212,6 +212,9 @@
 static_assert(ctrl_t::kDeleted == static_cast<ctrl_t>(-2),
               "ctrl_t::kDeleted must be -2 to make the implementation of "
               "ConvertSpecialToEmptyAndFullToDeleted efficient");
+static_assert(ctrl_t::kEmpty == static_cast<ctrl_t>(-128),
+              "ctrl_t::kEmpty must be -128 to use saturated subtraction in"
+              " ConvertSpecialToEmptyAndFullToDeleted");
 
 // Helpers for checking the state of a control byte.
 inline bool IsEmpty(ctrl_t c) { return c == ctrl_t::kEmpty; }
@@ -329,15 +332,15 @@
   }
 
   void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const {
+    // Take advantage of the fact that kEmpty is already the smallest signed
+    // char value, and using a saturated subtraction will not affect it.
+    // All special values have the MSB set, so after an AND with MSBS, we
+    // are left with -128 for special values and 0 for full. After applying
+    // subs 2, we arrive at the result of -128(kEmpty) for special and
+    // -2(kDeleted) for full.
     auto msbs = _mm_set1_epi8(static_cast<char>(-128));
-    auto x126 = _mm_set1_epi8(126);
-#ifdef ABSL_INTERNAL_HAVE_SSSE3
-    auto res = _mm_or_si128(_mm_shuffle_epi8(x126, ctrl), msbs);
-#else
-    auto zero = _mm_setzero_si128();
-    auto special_mask = _mm_cmpgt_epi8_fixed(zero, ctrl);
-    auto res = _mm_or_si128(msbs, _mm_andnot_si128(special_mask, x126));
-#endif
+    auto twos = _mm_set1_epi8(static_cast<char>(2));
+    auto res = _mm_subs_epi8(_mm_and_si128(msbs, ctrl), twos);
     _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), res);
   }
 
diff --git a/third_party/abseil-cpp/absl/container/internal/inlined_vector.h b/third_party/abseil-cpp/absl/container/internal/inlined_vector.h
index b0d3f077..85e8960 100644
--- a/third_party/abseil-cpp/absl/container/internal/inlined_vector.h
+++ b/third_party/abseil-cpp/absl/container/internal/inlined_vector.h
@@ -27,7 +27,6 @@
 
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
-#include "absl/base/internal/identity.h"
 #include "absl/base/macros.h"
 #include "absl/container/internal/compressed_tuple.h"
 #include "absl/memory/memory.h"
@@ -127,7 +126,7 @@
 };
 
 template <typename A, typename ValueAdapter>
-void ConstructElements(absl::internal::type_identity_t<A>& allocator,
+void ConstructElements(absl::type_identity_t<A>& allocator,
                        Pointer<A> construct_first, ValueAdapter& values,
                        SizeType<A> construct_size) {
   for (SizeType<A> i = 0; i < construct_size; ++i) {
diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h
index f3e2065d..9033982 100644
--- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h
+++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h
@@ -2445,17 +2445,17 @@
 
   template <class InputIt>
   void insert(InputIt first, InputIt last) {
-    for (; first != last; ++first) emplace(*first);
+    insert_range(first, last);
   }
 
   template <class T, RequiresNotInit<T> = 0,
             std::enable_if_t<Insertable<const T&>::value, int> = 0>
   void insert(std::initializer_list<T> ilist) {
-    insert(ilist.begin(), ilist.end());
+    insert_range(ilist.begin(), ilist.end());
   }
 
   void insert(std::initializer_list<init_type> ilist) {
-    insert(ilist.begin(), ilist.end());
+    insert_range(ilist.begin(), ilist.end());
   }
 
   insert_return_type insert(node_type&& node) ABSL_ATTRIBUTE_LIFETIME_BOUND {
@@ -3228,6 +3228,11 @@
     return {iterator_at(index), inserted};
   }
 
+  template <class InputIt>
+  void insert_range(InputIt first, InputIt last) {
+    for (; first != last; ++first) emplace(*first);
+  }
+
  protected:
   // Asserts for correctness that we run on find/find_or_prepare_insert.
   template <class K>
diff --git a/third_party/abseil-cpp/absl/functional/function_ref.h b/third_party/abseil-cpp/absl/functional/function_ref.h
index 10244b8..fe528537 100644
--- a/third_party/abseil-cpp/absl/functional/function_ref.h
+++ b/third_party/abseil-cpp/absl/functional/function_ref.h
@@ -48,6 +48,7 @@
 
 #include <cassert>
 #include <type_traits>
+#include <utility>
 
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
@@ -92,6 +93,15 @@
   using EnableIfCompatible =
       std::enable_if_t<std::is_invocable_r<R, F, U..., Args...>::value>;
 
+  // Internal constructor to supersede the copying constructor
+  template <typename F>
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  FunctionRef(std::in_place_t, F&& f ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept
+      : invoker_(&absl::functional_internal::InvokeObject<F&, R, Args...>) {
+    absl::functional_internal::AssertNonNull(f);
+    ptr_.obj = &f;
+  }
+
  public:
   // Constructs a FunctionRef from any invocable type.
   template <typename F,
@@ -99,10 +109,7 @@
                 !std::is_same_v<FunctionRef, absl::remove_cvref_t<F>>, F&>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
   FunctionRef(F&& f ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept
-      : invoker_(&absl::functional_internal::InvokeObject<F&, R, Args...>) {
-    absl::functional_internal::AssertNonNull(f);
-    ptr_.obj = &f;
-  }
+      : FunctionRef(std::in_place, std::forward<F>(f)) {}
 
   // Overload for function pointers. This eliminates a level of indirection that
   // would happen if the above overload was used (it lets us store the pointer
@@ -177,7 +184,8 @@
       typename = EnableIfCompatible<std::enable_if_t<
           !std::is_same_v<FunctionRef, absl::remove_cvref_t<F>>, const F&>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
-  FunctionRef(const F& f ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept : Base(f) {}
+  FunctionRef(const F& f ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept
+      : Base(std::in_place_t(), f) {}
 
   template <typename F, typename = EnableIfCompatible<F*>,
             absl::functional_internal::EnableIf<std::is_function_v<F>> = 0>
diff --git a/third_party/abseil-cpp/absl/functional/function_ref_test.cc b/third_party/abseil-cpp/absl/functional/function_ref_test.cc
index 26db103b..a0c67459 100644
--- a/third_party/abseil-cpp/absl/functional/function_ref_test.cc
+++ b/third_party/abseil-cpp/absl/functional/function_ref_test.cc
@@ -399,6 +399,18 @@
   EXPECT_EQ(1337, FunctionRef<int()>(s)());
 }
 
+TEST(FunctionRefTest, NonConstToConstConversion) {
+  // The const-qualified version might inherit from the non-const version.
+  // We want to make sure that this doesn't introduce a bug when an instance of
+  // the base (non-const) class is forwarded through the derived (const) class.
+  // This has the potential to trigger the copy constructor, thus incorrectly
+  // producing a copy rather than another indirection.
+  absl::FunctionRef<int()> a = +[]() { return 1; };
+  absl::FunctionRef<int() const> b = a;
+  a = []() { return 2; };
+  EXPECT_EQ(b(), 2);
+}
+
 }  // namespace
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/third_party/abseil-cpp/absl/meta/type_traits.h b/third_party/abseil-cpp/absl/meta/type_traits.h
index e2f4600..59eb38ba 100644
--- a/third_party/abseil-cpp/absl/meta/type_traits.h
+++ b/third_party/abseil-cpp/absl/meta/type_traits.h
@@ -169,6 +169,28 @@
 using remove_cvref_t = typename remove_cvref<T>::type;
 #endif
 
+#if defined(__cpp_lib_type_identity) && __cpp_lib_type_identity >= 201806L
+template <typename T>
+using type_identity = std::type_identity<T>;
+
+template <typename T>
+using type_identity_t = std::type_identity_t<T>;
+#else
+// type_identity
+//
+// Back-fill of C++20's `std::type_identity`.
+template <typename T>
+struct type_identity {
+  typedef T type;
+};
+
+// type_identity_t
+//
+// Back-fill of C++20's `std::type_identity_t`.
+template <typename T>
+using type_identity_t = typename type_identity<T>::type;
+#endif
+
 namespace type_traits_internal {
 
 #if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \
diff --git a/third_party/abseil-cpp/absl/meta/type_traits_test.cc b/third_party/abseil-cpp/absl/meta/type_traits_test.cc
index 81422903e..9a8262d 100644
--- a/third_party/abseil-cpp/absl/meta/type_traits_test.cc
+++ b/third_party/abseil-cpp/absl/meta/type_traits_test.cc
@@ -137,6 +137,17 @@
                             int[2]>::value));
 }
 
+TEST(TypeTraitsTest, TestTypeIdentity) {
+  EXPECT_TRUE((std::is_same_v<typename absl::type_identity<int>::type, int>));
+  EXPECT_TRUE((std::is_same_v<absl::type_identity_t<int>, int>));
+  EXPECT_TRUE((std::is_same_v<typename absl::type_identity<int&>::type, int&>));
+  EXPECT_TRUE((std::is_same_v<absl::type_identity_t<int&>, int&>));
+
+  EXPECT_FALSE(
+      (std::is_same_v<typename absl::type_identity<int64_t>::type, int32_t>));
+  EXPECT_FALSE((std::is_same_v<absl::type_identity_t<int64_t>, int32_t>));
+}
+
 struct TypeA {};
 struct TypeB {};
 struct TypeC {};
diff --git a/third_party/abseil-cpp/absl/synchronization/BUILD.bazel b/third_party/abseil-cpp/absl/synchronization/BUILD.bazel
index 9a1aa83..9c6b31a2 100644
--- a/third_party/abseil-cpp/absl/synchronization/BUILD.bazel
+++ b/third_party/abseil-cpp/absl/synchronization/BUILD.bazel
@@ -148,6 +148,7 @@
         "//absl/base:tracing_internal",
         "//absl/debugging:stacktrace",
         "//absl/debugging:symbolize",
+        "//absl/meta:type_traits",
         "//absl/time",
     ] + select({
         "//conditions:default": [],
diff --git a/third_party/abseil-cpp/absl/synchronization/BUILD.gn b/third_party/abseil-cpp/absl/synchronization/BUILD.gn
index 1fdeab4..34274bc 100644
--- a/third_party/abseil-cpp/absl/synchronization/BUILD.gn
+++ b/third_party/abseil-cpp/absl/synchronization/BUILD.gn
@@ -87,6 +87,7 @@
     "//third_party/abseil-cpp/absl/base:tracing_internal",
     "//third_party/abseil-cpp/absl/debugging:stacktrace",
     "//third_party/abseil-cpp/absl/debugging:symbolize",
+    "//third_party/abseil-cpp/absl/meta:type_traits",
     "//third_party/abseil-cpp/absl/time",
   ]
 }
diff --git a/third_party/abseil-cpp/absl/synchronization/CMakeLists.txt b/third_party/abseil-cpp/absl/synchronization/CMakeLists.txt
index ad45515c..9c4a0b1f 100644
--- a/third_party/abseil-cpp/absl/synchronization/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/synchronization/CMakeLists.txt
@@ -109,6 +109,7 @@
     absl::core_headers
     absl::dynamic_annotations
     absl::malloc_internal
+    absl::meta
     absl::nullability
     absl::raw_logging_internal
     absl::stacktrace
diff --git a/third_party/abseil-cpp/absl/synchronization/mutex.h b/third_party/abseil-cpp/absl/synchronization/mutex.h
index 2062486..10fafcb 100644
--- a/third_party/abseil-cpp/absl/synchronization/mutex.h
+++ b/third_party/abseil-cpp/absl/synchronization/mutex.h
@@ -65,12 +65,12 @@
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/base/const_init.h"
-#include "absl/base/internal/identity.h"
 #include "absl/base/internal/thread_identity.h"
 #include "absl/base/internal/tsan_mutex_interface.h"
 #include "absl/base/macros.h"
 #include "absl/base/nullability.h"
 #include "absl/base/thread_annotations.h"
+#include "absl/meta/type_traits.h"
 #include "absl/synchronization/internal/kernel_timeout.h"
 #include "absl/synchronization/internal/per_thread_sem.h"
 #include "absl/time/time.h"
@@ -794,7 +794,7 @@
   template <typename T, typename = void>
   Condition(
       bool (*absl_nonnull func)(T* absl_nullability_unknown),
-      typename absl::internal::type_identity<T>::type* absl_nullability_unknown
+      typename absl::type_identity<T>::type* absl_nullability_unknown
           arg);
 
   // Templated version for invoking a method that returns a `bool`.
@@ -802,19 +802,19 @@
   // `Condition(object, &Class::Method)` constructs a `Condition` that evaluates
   // `object->Method()`.
   //
-  // Implementation Note: `absl::internal::type_identity` is used to allow
+  // Implementation Note: `absl::type_identity` is used to allow
   // methods to come from base classes. A simpler signature like
   // `Condition(T*, bool (T::*)())` does not suffice.
   template <typename T>
   Condition(
       T* absl_nonnull object,
-      bool (absl::internal::type_identity<T>::type::* absl_nonnull method)());
+      bool (absl::type_identity<T>::type::* absl_nonnull method)());
 
   // Same as above, for const members
   template <typename T>
   Condition(
       const T* absl_nonnull object,
-      bool (absl::internal::type_identity<T>::type::* absl_nonnull method)()
+      bool (absl::type_identity<T>::type::* absl_nonnull method)()
           const);
 
   // A Condition that returns the value of `*cond`
@@ -1183,7 +1183,7 @@
 template <typename T, typename>
 inline Condition::Condition(
     bool (*absl_nonnull func)(T* absl_nullability_unknown),
-    typename absl::internal::type_identity<T>::type* absl_nullability_unknown
+    typename absl::type_identity<T>::type* absl_nullability_unknown
         arg)
     // Just delegate to the overload above.
     : Condition(func, arg) {}
@@ -1191,7 +1191,7 @@
 template <typename T>
 inline Condition::Condition(
     T* absl_nonnull object,
-    bool (absl::internal::type_identity<T>::type::* absl_nonnull method)())
+    bool (absl::type_identity<T>::type::* absl_nonnull method)())
     : eval_(&CastAndCallMethod<T, decltype(method)>), arg_(object) {
   static_assert(sizeof(&method) <= sizeof(callback_),
                 "An overlarge method pointer was passed to Condition.");
@@ -1201,7 +1201,7 @@
 template <typename T>
 inline Condition::Condition(
     const T* absl_nonnull object,
-    bool (absl::internal::type_identity<T>::type::* absl_nonnull method)()
+    bool (absl::type_identity<T>::type::* absl_nonnull method)()
         const)
     : eval_(&CastAndCallMethod<const T, decltype(method)>),
       arg_(reinterpret_cast<void*>(const_cast<T*>(object))) {
diff --git a/third_party/abseil-cpp/symbols_arm64_dbg.def b/third_party/abseil-cpp/symbols_arm64_dbg.def
index 602a39d..2bdd663 100644
--- a/third_party/abseil-cpp/symbols_arm64_dbg.def
+++ b/third_party/abseil-cpp/symbols_arm64_dbg.def
@@ -143,6 +143,7 @@
     ??$?0PEBUPrefixCrc@CrcCordState@crc_internal@absl@@PEBU0123@$0A@@?$pair@PEBUPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@@__Cr@std@@QEAA@$$QEAPEBUPrefixCrc@CrcCordState@crc_internal@absl@@0@Z
     ??$?0PEBUPrefixCrc@CrcCordState@crc_internal@absl@@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEAU1234@AEAU1234@PEAPEAU1234@_J$0A@@__Cr@std@@$0A@@?$pair@PEBUPrefixCrc@CrcCordState@crc_internal@absl@@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEAU1234@AEAU1234@PEAPEAU1234@_J$0A@@__Cr@std@@@__Cr@std@@QEAA@$$QEAPEBUPrefixCrc@CrcCordState@crc_internal@absl@@$$QEAV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEAU1234@AEAU1234@PEAPEAU1234@_J$0A@@12@@Z
     ??$?0PEBVFormatArgImpl@str_format_internal@absl@@$0A@@?$InlinedVector@VFormatArgImpl@str_format_internal@absl@@$03V?$allocator@VFormatArgImpl@str_format_internal@absl@@@__Cr@std@@@absl@@QEAA@PEBVFormatArgImpl@str_format_internal@1@0AEBV?$allocator@VFormatArgImpl@str_format_internal@absl@@@__Cr@std@@@Z
+    ??$?0U?$HashKey@UStringHash@container_internal@absl@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@$00@container_internal@absl@@@?$FunctionRef@$$A6A_K_K@Z@absl@@IEAA@Uin_place_t@__Cr@std@@$$QEAU?$HashKey@UStringHash@container_internal@absl@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@$00@container_internal@1@@Z
     ??$?0U?$HashKey@UStringHash@container_internal@absl@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@$00@container_internal@absl@@X@?$FunctionRef@$$A6A_K_K@Z@absl@@QEAA@$$QEAU?$HashKey@UStringHash@container_internal@absl@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@$00@container_internal@1@@Z
     ??$?0U?$__check_pair_construction@UFindInfo@container_internal@absl@@V?$NonIterableBitMask@_K$07$02@23@@__Cr@std@@$0A@@?$pair@UFindInfo@container_internal@absl@@V?$NonIterableBitMask@_K$07$02@23@@__Cr@std@@QEAA@AEBUFindInfo@container_internal@absl@@AEBV?$NonIterableBitMask@_K$07$02@45@@Z
     ??$?0U?$array@D$0DKJI@@__Cr@std@@XU012@H@?$Span@D@absl@@QEAA@AEAU?$array@D$0DKJI@@__Cr@std@@@Z
@@ -846,6 +847,8 @@
     ??$__annotate_contiguous_container@V?$allocator@V?$unique_ptr@VFlagStateInterface@flags_internal@absl@@U?$default_delete@VFlagStateInterface@flags_internal@absl@@@__Cr@std@@@__Cr@std@@@__Cr@std@@@__Cr@std@@YAXPEBX000@Z
     ??$__annotate_double_ended_contiguous_container@V?$allocator@UPrefixCrc@CrcCordState@crc_internal@absl@@@__Cr@std@@@__Cr@std@@YAXPEBX00000@Z
     ??$__append_with_size@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@__Cr@std@@@?$deque@UPrefixCrc@CrcCordState@crc_internal@absl@@V?$allocator@UPrefixCrc@CrcCordState@crc_internal@absl@@@__Cr@std@@@__Cr@std@@AEAAXV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@12@_K@Z
+    ??$__as_transparent@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@$0A@@__Cr@std@@YA?AV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@01@AEAV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@01@@Z
+    ??$__as_transparent@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@$0A@@__Cr@std@@YA?AV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@01@AEAV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@01@@Z
     ??$__as_variant@V?$variant@_K_JIH_N@__Cr@std@@V?$variant@_K_JN@23@V?$Span@$$CBD@absl@@V?$variant@IHM@23@@__Cr@std@@YAAEAV?$variant@V?$variant@_K_JIH_N@__Cr@std@@V?$variant@_K_JN@23@V?$Span@$$CBD@absl@@V?$variant@IHM@23@@01@AEAV201@@Z
     ??$__assign_with_size_random_access@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@__Cr@std@@@?$deque@UPrefixCrc@CrcCordState@crc_internal@absl@@V?$allocator@UPrefixCrc@CrcCordState@crc_internal@absl@@@__Cr@std@@@__Cr@std@@AEAAXV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@12@_J@Z
     ??$__at@P6A_K$$QEAU?$__value_visitor@UBufferSizeVisitor@?1??BufferSizeForStructuredProtoField@log_internal@absl@@YA_KUStructuredProtoField@34@@Z@@__variant@__visitation@__variant_detail@__Cr@std@@AEAV?$__base@$0A@V?$variant@_K_JIH_N@__Cr@std@@V?$variant@_K_JN@23@V?$Span@$$CBD@absl@@V?$variant@IHM@23@@456@@Z$03$$V@__base@__visitation@__variant_detail@__Cr@std@@CA$$QEA_PAEBU?$__farray@P6A_K$$QEAU?$__value_visitor@UBufferSizeVisitor@?1??BufferSizeForStructuredProtoField@log_internal@absl@@YA_KUStructuredProtoField@34@@Z@@__variant@__visitation@__variant_detail@__Cr@std@@AEAV?$__base@$0A@V?$variant@_K_JIH_N@__Cr@std@@V?$variant@_K_JN@23@V?$Span@$$CBD@absl@@V?$variant@IHM@23@@456@@Z$03@34@_K@Z
@@ -1737,12 +1740,14 @@
     ??0?$__hash_node_destructor@V?$allocator@U?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@__Cr@std@@@__Cr@std@@@__Cr@std@@QEAA@AEAV?$allocator@U?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@__Cr@std@@@12@_N@Z
     ??0?$__hash_table@PEBUCordRep@cord_internal@absl@@U?$hash@PEBUCordRep@cord_internal@absl@@@__Cr@std@@U?$equal_to@PEBUCordRep@cord_internal@absl@@@56@V?$allocator@PEBUCordRep@cord_internal@absl@@@56@@__Cr@std@@QEAA@XZ
     ??0?$__hash_table@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@V?$__unordered_map_hasher@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@V?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@@23@@__Cr@std@@QEAA@XZ
-    ??0?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@X@__Cr@std@@QEAA@AEBV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@12@@Z
-    ??0?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@X@__Cr@std@@QEAA@AEBV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@12@@Z
+    ??0?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@X@__Cr@std@@QEAA@AEBV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@12@@Z
+    ??0?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@X@__Cr@std@@QEAA@AEBV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@12@@Z
     ??0?$__map_iterator@V?$__tree_iterator@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@__Cr@std@@PEAV?$__tree_node@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@__Cr@std@@PEAX@23@_J@__Cr@std@@@__Cr@std@@QEAA@V?$__tree_iterator@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@__Cr@std@@PEAV?$__tree_node@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@__Cr@std@@PEAX@23@_J@12@@Z
     ??0?$__map_iterator@V?$__tree_iterator@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@PEAV?$__tree_node@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@PEAX@23@_J@__Cr@std@@@__Cr@std@@QEAA@V?$__tree_iterator@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@PEAV?$__tree_node@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@PEAX@23@_J@12@@Z
     ??0?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@QEAA@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@12@@Z
+    ??0?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@__Cr@std@@QEAA@XZ
     ??0?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@QEAA@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@12@@Z
+    ??0?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@__Cr@std@@QEAA@XZ
     ??0?$__non_trivial_if@$00V?$allocator@PEAPEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@@__Cr@std@@QEAA@XZ
     ??0?$__non_trivial_if@$00V?$allocator@PEAU?$__hash_node_base@PEAU?$__hash_node@PEBUCordRep@cord_internal@absl@@PEAX@__Cr@std@@@__Cr@std@@@__Cr@std@@@__Cr@std@@QEAA@XZ
     ??0?$__non_trivial_if@$00V?$allocator@PEAU?$__hash_node_base@PEAU?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@__Cr@std@@@__Cr@std@@@__Cr@std@@@__Cr@std@@QEAA@XZ
@@ -2702,8 +2707,8 @@
     ??R?$__bucket_list_deallocator@V?$allocator@PEAU?$__hash_node_base@PEAU?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@__Cr@std@@@__Cr@std@@@__Cr@std@@@__Cr@std@@QEAAXPEAPEAU?$__hash_node_base@PEAU?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@__Cr@std@@@12@@Z
     ??R?$__hash_node_destructor@V?$allocator@U?$__hash_node@PEBUCordRep@cord_internal@absl@@PEAX@__Cr@std@@@__Cr@std@@@__Cr@std@@QEAAXPEAU?$__hash_node@PEBUCordRep@cord_internal@absl@@PEAX@12@@Z
     ??R?$__hash_node_destructor@V?$allocator@U?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@__Cr@std@@@__Cr@std@@@__Cr@std@@QEAAXPEAU?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@12@@Z
-    ??R?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@X@__Cr@std@@QEBA@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@12@AEBU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@12@@Z
-    ??R?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@X@__Cr@std@@QEBA@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@12@AEBU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@12@@Z
+    ??R?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@X@__Cr@std@@QEBA@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@12@AEBU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@12@@Z
+    ??R?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@X@__Cr@std@@QEBA@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@12@AEBU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@12@@Z
     ??R?$__policy_func@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@__Cr@std@@@__Cr@std@@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@@Z@__function@__Cr@std@@QEBA?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@__Cr@std@@@23@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@@Z
     ??R?$__policy_func@$$A6AXAEAVCommandLineFlag@absl@@@Z@__function@__Cr@std@@QEBAXAEAVCommandLineFlag@absl@@@Z
     ??R?$__policy_func@$$A6A_NAEBVCommandLineFlag@absl@@@Z@__function@__Cr@std@@QEBA_NAEBVCommandLineFlag@absl@@@Z
@@ -5359,8 +5364,8 @@
     ?iterator_at@?$raw_hash_set@U?$FlatHashMapPolicy@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAVCommandLineFlag@absl@@@container_internal@absl@@UStringHash@23@UStringEq@23@V?$allocator@U?$pair@$$CBV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAVCommandLineFlag@absl@@@__Cr@std@@@__Cr@std@@@container_internal@absl@@IEAA?AViterator@123@_K@Z
     ?iterator_at_ptr@?$raw_hash_set@U?$FlatHashMapPolicy@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAVCommandLineFlag@absl@@@container_internal@absl@@UStringHash@23@UStringEq@23@V?$allocator@U?$pair@$$CBV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAVCommandLineFlag@absl@@@__Cr@std@@@__Cr@std@@@container_internal@absl@@IEAA?AViterator@123@U?$pair@PEAW4ctrl_t@container_internal@absl@@PEAX@__Cr@std@@@Z
     ?kDefaultIterControl@container_internal@absl@@3W4ctrl_t@12@A
-    ?key_comp@?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@QEBAAEBU?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@XZ
-    ?key_comp@?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@QEBAAEBU?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@XZ
+    ?key_comp@?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@__Cr@std@@QEBAAEBU?$less@X@23@XZ
+    ?key_comp@?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@__Cr@std@@QEBAAEBU?$less@X@23@XZ
     ?key_eq@?$__hash_table@PEBUCordRep@cord_internal@absl@@U?$hash@PEBUCordRep@cord_internal@absl@@@__Cr@std@@U?$equal_to@PEBUCordRep@cord_internal@absl@@@56@V?$allocator@PEBUCordRep@cord_internal@absl@@@56@@__Cr@std@@QEAAAEAU?$equal_to@PEBUCordRep@cord_internal@absl@@@23@XZ
     ?key_eq@?$__hash_table@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@V?$__unordered_map_hasher@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@V?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@@23@@__Cr@std@@QEAAAEAV?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@XZ
     ?length@CordRepBtreeReader@cord_internal@absl@@QEBA_KXZ
diff --git a/third_party/abseil-cpp/symbols_x64_dbg.def b/third_party/abseil-cpp/symbols_x64_dbg.def
index cbb9da84..75b9f27 100644
--- a/third_party/abseil-cpp/symbols_x64_dbg.def
+++ b/third_party/abseil-cpp/symbols_x64_dbg.def
@@ -143,6 +143,7 @@
     ??$?0PEBUPrefixCrc@CrcCordState@crc_internal@absl@@PEBU0123@$0A@@?$pair@PEBUPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@@__Cr@std@@QEAA@$$QEAPEBUPrefixCrc@CrcCordState@crc_internal@absl@@0@Z
     ??$?0PEBUPrefixCrc@CrcCordState@crc_internal@absl@@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEAU1234@AEAU1234@PEAPEAU1234@_J$0A@@__Cr@std@@$0A@@?$pair@PEBUPrefixCrc@CrcCordState@crc_internal@absl@@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEAU1234@AEAU1234@PEAPEAU1234@_J$0A@@__Cr@std@@@__Cr@std@@QEAA@$$QEAPEBUPrefixCrc@CrcCordState@crc_internal@absl@@$$QEAV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEAU1234@AEAU1234@PEAPEAU1234@_J$0A@@12@@Z
     ??$?0PEBVFormatArgImpl@str_format_internal@absl@@$0A@@?$InlinedVector@VFormatArgImpl@str_format_internal@absl@@$03V?$allocator@VFormatArgImpl@str_format_internal@absl@@@__Cr@std@@@absl@@QEAA@PEBVFormatArgImpl@str_format_internal@1@0AEBV?$allocator@VFormatArgImpl@str_format_internal@absl@@@__Cr@std@@@Z
+    ??$?0U?$HashKey@UStringHash@container_internal@absl@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@$00@container_internal@absl@@@?$FunctionRef@$$A6A_K_K@Z@absl@@IEAA@Uin_place_t@__Cr@std@@$$QEAU?$HashKey@UStringHash@container_internal@absl@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@$00@container_internal@1@@Z
     ??$?0U?$HashKey@UStringHash@container_internal@absl@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@$00@container_internal@absl@@X@?$FunctionRef@$$A6A_K_K@Z@absl@@QEAA@$$QEAU?$HashKey@UStringHash@container_internal@absl@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@$00@container_internal@1@@Z
     ??$?0U?$__check_pair_construction@UFindInfo@container_internal@absl@@V?$NonIterableBitMask@G$0BA@$0A@@23@@__Cr@std@@$0A@@?$pair@UFindInfo@container_internal@absl@@V?$NonIterableBitMask@G$0BA@$0A@@23@@__Cr@std@@QEAA@AEBUFindInfo@container_internal@absl@@AEBV?$NonIterableBitMask@G$0BA@$0A@@45@@Z
     ??$?0U?$array@D$0DKJI@@__Cr@std@@XU012@H@?$Span@D@absl@@QEAA@AEAU?$array@D$0DKJI@@__Cr@std@@@Z
@@ -846,6 +847,8 @@
     ??$__annotate_contiguous_container@V?$allocator@V?$unique_ptr@VFlagStateInterface@flags_internal@absl@@U?$default_delete@VFlagStateInterface@flags_internal@absl@@@__Cr@std@@@__Cr@std@@@__Cr@std@@@__Cr@std@@YAXPEBX000@Z
     ??$__annotate_double_ended_contiguous_container@V?$allocator@UPrefixCrc@CrcCordState@crc_internal@absl@@@__Cr@std@@@__Cr@std@@YAXPEBX00000@Z
     ??$__append_with_size@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@__Cr@std@@@?$deque@UPrefixCrc@CrcCordState@crc_internal@absl@@V?$allocator@UPrefixCrc@CrcCordState@crc_internal@absl@@@__Cr@std@@@__Cr@std@@AEAAXV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@12@_K@Z
+    ??$__as_transparent@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@$0A@@__Cr@std@@YA?AV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@01@AEAV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@01@@Z
+    ??$__as_transparent@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@$0A@@__Cr@std@@YA?AV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@01@AEAV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@01@@Z
     ??$__as_variant@V?$variant@_K_JIH_N@__Cr@std@@V?$variant@_K_JN@23@V?$Span@$$CBD@absl@@V?$variant@IHM@23@@__Cr@std@@YAAEAV?$variant@V?$variant@_K_JIH_N@__Cr@std@@V?$variant@_K_JN@23@V?$Span@$$CBD@absl@@V?$variant@IHM@23@@01@AEAV201@@Z
     ??$__assign_with_size_random_access@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@__Cr@std@@@?$deque@UPrefixCrc@CrcCordState@crc_internal@absl@@V?$allocator@UPrefixCrc@CrcCordState@crc_internal@absl@@@__Cr@std@@@__Cr@std@@AEAAXV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PEBU1234@AEBU1234@PEBQEBU1234@_J$0A@@12@_J@Z
     ??$__at@P6A_K$$QEAU?$__value_visitor@UBufferSizeVisitor@?1??BufferSizeForStructuredProtoField@log_internal@absl@@YA_KUStructuredProtoField@34@@Z@@__variant@__visitation@__variant_detail@__Cr@std@@AEAV?$__base@$0A@V?$variant@_K_JIH_N@__Cr@std@@V?$variant@_K_JN@23@V?$Span@$$CBD@absl@@V?$variant@IHM@23@@456@@Z$03$$V@__base@__visitation@__variant_detail@__Cr@std@@CA$$QEA_PAEBU?$__farray@P6A_K$$QEAU?$__value_visitor@UBufferSizeVisitor@?1??BufferSizeForStructuredProtoField@log_internal@absl@@YA_KUStructuredProtoField@34@@Z@@__variant@__visitation@__variant_detail@__Cr@std@@AEAV?$__base@$0A@V?$variant@_K_JIH_N@__Cr@std@@V?$variant@_K_JN@23@V?$Span@$$CBD@absl@@V?$variant@IHM@23@@456@@Z$03@34@_K@Z
@@ -1738,12 +1741,14 @@
     ??0?$__hash_node_destructor@V?$allocator@U?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@__Cr@std@@@__Cr@std@@@__Cr@std@@QEAA@AEAV?$allocator@U?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@__Cr@std@@@12@_N@Z
     ??0?$__hash_table@PEBUCordRep@cord_internal@absl@@U?$hash@PEBUCordRep@cord_internal@absl@@@__Cr@std@@U?$equal_to@PEBUCordRep@cord_internal@absl@@@56@V?$allocator@PEBUCordRep@cord_internal@absl@@@56@@__Cr@std@@QEAA@XZ
     ??0?$__hash_table@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@V?$__unordered_map_hasher@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@V?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@@23@@__Cr@std@@QEAA@XZ
-    ??0?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@X@__Cr@std@@QEAA@AEBV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@12@@Z
-    ??0?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@X@__Cr@std@@QEAA@AEBV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@12@@Z
+    ??0?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@X@__Cr@std@@QEAA@AEBV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@12@@Z
+    ??0?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@X@__Cr@std@@QEAA@AEBV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@12@@Z
     ??0?$__map_iterator@V?$__tree_iterator@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@__Cr@std@@PEAV?$__tree_node@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@__Cr@std@@PEAX@23@_J@__Cr@std@@@__Cr@std@@QEAA@V?$__tree_iterator@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@__Cr@std@@PEAV?$__tree_node@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@__Cr@std@@PEAX@23@_J@12@@Z
     ??0?$__map_iterator@V?$__tree_iterator@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@PEAV?$__tree_node@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@PEAX@23@_J@__Cr@std@@@__Cr@std@@QEAA@V?$__tree_iterator@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@PEAV?$__tree_node@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@PEAX@23@_J@12@@Z
     ??0?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@QEAA@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@12@@Z
+    ??0?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@__Cr@std@@QEAA@XZ
     ??0?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@QEAA@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@12@@Z
+    ??0?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@__Cr@std@@QEAA@XZ
     ??0?$__non_trivial_if@$00V?$allocator@PEAPEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@@__Cr@std@@QEAA@XZ
     ??0?$__non_trivial_if@$00V?$allocator@PEAU?$__hash_node_base@PEAU?$__hash_node@PEBUCordRep@cord_internal@absl@@PEAX@__Cr@std@@@__Cr@std@@@__Cr@std@@@__Cr@std@@QEAA@XZ
     ??0?$__non_trivial_if@$00V?$allocator@PEAU?$__hash_node_base@PEAU?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@__Cr@std@@@__Cr@std@@@__Cr@std@@@__Cr@std@@QEAA@XZ
@@ -2703,8 +2708,8 @@
     ??R?$__bucket_list_deallocator@V?$allocator@PEAU?$__hash_node_base@PEAU?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@__Cr@std@@@__Cr@std@@@__Cr@std@@@__Cr@std@@QEAAXPEAPEAU?$__hash_node_base@PEAU?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@__Cr@std@@@12@@Z
     ??R?$__hash_node_destructor@V?$allocator@U?$__hash_node@PEBUCordRep@cord_internal@absl@@PEAX@__Cr@std@@@__Cr@std@@@__Cr@std@@QEAAXPEAU?$__hash_node@PEBUCordRep@cord_internal@absl@@PEAX@12@@Z
     ??R?$__hash_node_destructor@V?$allocator@U?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@__Cr@std@@@__Cr@std@@@__Cr@std@@QEAAXPEAU?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PEAX@12@@Z
-    ??R?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@X@__Cr@std@@QEBA@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@12@AEBU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@12@@Z
-    ??R?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@X@__Cr@std@@QEBA@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@12@AEBU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@12@@Z
+    ??R?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@X@__Cr@std@@QEBA@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@12@AEBU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@12@@Z
+    ??R?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@X@__Cr@std@@QEBA@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@12@AEBU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@12@@Z
     ??R?$__policy_func@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@__Cr@std@@@__Cr@std@@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@@Z@__function@__Cr@std@@QEBA?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@__Cr@std@@@23@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@@Z
     ??R?$__policy_func@$$A6AXAEAVCommandLineFlag@absl@@@Z@__function@__Cr@std@@QEBAXAEAVCommandLineFlag@absl@@@Z
     ??R?$__policy_func@$$A6A_NAEBVCommandLineFlag@absl@@@Z@__function@__Cr@std@@QEBA_NAEBVCommandLineFlag@absl@@@Z
@@ -5363,8 +5368,8 @@
     ?iterator_at@?$raw_hash_set@U?$FlatHashMapPolicy@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAVCommandLineFlag@absl@@@container_internal@absl@@UStringHash@23@UStringEq@23@V?$allocator@U?$pair@$$CBV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAVCommandLineFlag@absl@@@__Cr@std@@@__Cr@std@@@container_internal@absl@@IEAA?AViterator@123@_K@Z
     ?iterator_at_ptr@?$raw_hash_set@U?$FlatHashMapPolicy@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAVCommandLineFlag@absl@@@container_internal@absl@@UStringHash@23@UStringEq@23@V?$allocator@U?$pair@$$CBV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAVCommandLineFlag@absl@@@__Cr@std@@@__Cr@std@@@container_internal@absl@@IEAA?AViterator@123@U?$pair@PEAW4ctrl_t@container_internal@absl@@PEAX@__Cr@std@@@Z
     ?kDefaultIterControl@container_internal@absl@@3W4ctrl_t@12@A
-    ?key_comp@?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@QEBAAEBU?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@XZ
-    ?key_comp@?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@QEBAAEBU?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@XZ
+    ?key_comp@?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@__Cr@std@@QEBAAEBU?$less@X@23@XZ
+    ?key_comp@?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PEBVCommandLineFlag@absl@@V?$allocator@PEBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@__Cr@std@@QEBAAEBU?$less@X@23@XZ
     ?key_eq@?$__hash_table@PEBUCordRep@cord_internal@absl@@U?$hash@PEBUCordRep@cord_internal@absl@@@__Cr@std@@U?$equal_to@PEBUCordRep@cord_internal@absl@@@56@V?$allocator@PEBUCordRep@cord_internal@absl@@@56@@__Cr@std@@QEAAAEAU?$equal_to@PEBUCordRep@cord_internal@absl@@@23@XZ
     ?key_eq@?$__hash_table@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@V?$__unordered_map_hasher@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@V?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@@23@@__Cr@std@@QEAAAEAV?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@XZ
     ?length@CordRepBtreeReader@cord_internal@absl@@QEBA_KXZ
diff --git a/third_party/abseil-cpp/symbols_x86_dbg.def b/third_party/abseil-cpp/symbols_x86_dbg.def
index b7042c5..f081f2a 100644
--- a/third_party/abseil-cpp/symbols_x86_dbg.def
+++ b/third_party/abseil-cpp/symbols_x86_dbg.def
@@ -143,6 +143,7 @@
     ??$?0PBUPrefixCrc@CrcCordState@crc_internal@absl@@PBU0123@$0A@@?$pair@PBUPrefixCrc@CrcCordState@crc_internal@absl@@PBU1234@@__Cr@std@@QAE@$$QAPBUPrefixCrc@CrcCordState@crc_internal@absl@@0@Z
     ??$?0PBUPrefixCrc@CrcCordState@crc_internal@absl@@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PAU1234@AAU1234@PAPAU1234@H$0A@@__Cr@std@@$0A@@?$pair@PBUPrefixCrc@CrcCordState@crc_internal@absl@@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PAU1234@AAU1234@PAPAU1234@H$0A@@__Cr@std@@@__Cr@std@@QAE@$$QAPBUPrefixCrc@CrcCordState@crc_internal@absl@@$$QAV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PAU1234@AAU1234@PAPAU1234@H$0A@@12@@Z
     ??$?0PBVFormatArgImpl@str_format_internal@absl@@$0A@@?$InlinedVector@VFormatArgImpl@str_format_internal@absl@@$03V?$allocator@VFormatArgImpl@str_format_internal@absl@@@__Cr@std@@@absl@@QAE@PBVFormatArgImpl@str_format_internal@1@0ABV?$allocator@VFormatArgImpl@str_format_internal@absl@@@__Cr@std@@@Z
+    ??$?0U?$HashKey@UStringHash@container_internal@absl@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@$00@container_internal@absl@@@?$FunctionRef@$$A6AII@Z@absl@@IAE@Uin_place_t@__Cr@std@@$$QAU?$HashKey@UStringHash@container_internal@absl@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@$00@container_internal@1@@Z
     ??$?0U?$HashKey@UStringHash@container_internal@absl@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@$00@container_internal@absl@@X@?$FunctionRef@$$A6AII@Z@absl@@QAE@$$QAU?$HashKey@UStringHash@container_internal@absl@@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@$00@container_internal@1@@Z
     ??$?0U?$__check_pair_construction@UFindInfo@container_internal@absl@@V?$NonIterableBitMask@G$0BA@$0A@@23@@__Cr@std@@$0A@@?$pair@UFindInfo@container_internal@absl@@V?$NonIterableBitMask@G$0BA@$0A@@23@@__Cr@std@@QAE@ABUFindInfo@container_internal@absl@@ABV?$NonIterableBitMask@G$0BA@$0A@@45@@Z
     ??$?0U?$array@D$0DKJI@@__Cr@std@@XU012@H@?$Span@D@absl@@QAE@AAU?$array@D$0DKJI@@__Cr@std@@@Z
@@ -844,6 +845,8 @@
     ??$__annotate_contiguous_container@V?$allocator@V?$unique_ptr@VFlagStateInterface@flags_internal@absl@@U?$default_delete@VFlagStateInterface@flags_internal@absl@@@__Cr@std@@@__Cr@std@@@__Cr@std@@@__Cr@std@@YAXPBX000@Z
     ??$__annotate_double_ended_contiguous_container@V?$allocator@UPrefixCrc@CrcCordState@crc_internal@absl@@@__Cr@std@@@__Cr@std@@YAXPBX00000@Z
     ??$__append_with_size@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PBU1234@ABU1234@PBQBU1234@H$0A@@__Cr@std@@@?$deque@UPrefixCrc@CrcCordState@crc_internal@absl@@V?$allocator@UPrefixCrc@CrcCordState@crc_internal@absl@@@__Cr@std@@@__Cr@std@@AAEXV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PBU1234@ABU1234@PBQBU1234@H$0A@@12@I@Z
+    ??$__as_transparent@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@$0A@@__Cr@std@@YA?AV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@01@AAV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@01@@Z
+    ??$__as_transparent@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@$0A@@__Cr@std@@YA?AV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@01@AAV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@01@@Z
     ??$__as_variant@V?$variant@_K_JIH_N@__Cr@std@@V?$variant@_K_JN@23@V?$Span@$$CBD@absl@@V?$variant@IHM@23@@__Cr@std@@YAAAV?$variant@V?$variant@_K_JIH_N@__Cr@std@@V?$variant@_K_JN@23@V?$Span@$$CBD@absl@@V?$variant@IHM@23@@01@AAV201@@Z
     ??$__assign_with_size_random_access@V?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PBU1234@ABU1234@PBQBU1234@H$0A@@__Cr@std@@@?$deque@UPrefixCrc@CrcCordState@crc_internal@absl@@V?$allocator@UPrefixCrc@CrcCordState@crc_internal@absl@@@__Cr@std@@@__Cr@std@@AAEXV?$__deque_iterator@UPrefixCrc@CrcCordState@crc_internal@absl@@PBU1234@ABU1234@PBQBU1234@H$0A@@12@H@Z
     ??$__at@P6AI$$QAU?$__value_visitor@UBufferSizeVisitor@?1??BufferSizeForStructuredProtoField@log_internal@absl@@YAIUStructuredProtoField@34@@Z@@__variant@__visitation@__variant_detail@__Cr@std@@AAV?$__base@$0A@V?$variant@_K_JIH_N@__Cr@std@@V?$variant@_K_JN@23@V?$Span@$$CBD@absl@@V?$variant@IHM@23@@456@@Z$03$$V@__base@__visitation@__variant_detail@__Cr@std@@CA$$QA_PABU?$__farray@P6AI$$QAU?$__value_visitor@UBufferSizeVisitor@?1??BufferSizeForStructuredProtoField@log_internal@absl@@YAIUStructuredProtoField@34@@Z@@__variant@__visitation@__variant_detail@__Cr@std@@AAV?$__base@$0A@V?$variant@_K_JIH_N@__Cr@std@@V?$variant@_K_JN@23@V?$Span@$$CBD@absl@@V?$variant@IHM@23@@456@@Z$03@34@I@Z
@@ -1736,12 +1739,14 @@
     ??0?$__hash_node_destructor@V?$allocator@U?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PAX@__Cr@std@@@__Cr@std@@@__Cr@std@@QAE@AAV?$allocator@U?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PAX@__Cr@std@@@12@_N@Z
     ??0?$__hash_table@PBUCordRep@cord_internal@absl@@U?$hash@PBUCordRep@cord_internal@absl@@@__Cr@std@@U?$equal_to@PBUCordRep@cord_internal@absl@@@56@V?$allocator@PBUCordRep@cord_internal@absl@@@56@@__Cr@std@@QAE@XZ
     ??0?$__hash_table@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@V?$__unordered_map_hasher@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@V?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@@23@@__Cr@std@@QAE@XZ
-    ??0?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@X@__Cr@std@@QAE@ABV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@12@@Z
-    ??0?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@X@__Cr@std@@QAE@ABV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@12@@Z
+    ??0?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@X@__Cr@std@@QAE@ABV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@12@@Z
+    ??0?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@X@__Cr@std@@QAE@ABV?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@12@@Z
     ??0?$__map_iterator@V?$__tree_iterator@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@__Cr@std@@PAV?$__tree_node@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@__Cr@std@@PAX@23@H@__Cr@std@@@__Cr@std@@QAE@V?$__tree_iterator@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@__Cr@std@@PAV?$__tree_node@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@__Cr@std@@PAX@23@H@12@@Z
     ??0?$__map_iterator@V?$__tree_iterator@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@PAV?$__tree_node@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@PAX@23@H@__Cr@std@@@__Cr@std@@QAE@V?$__tree_iterator@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@PAV?$__tree_node@U?$__value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@PAX@23@H@12@@Z
     ??0?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@QAE@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@12@@Z
+    ??0?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@__Cr@std@@QAE@XZ
     ??0?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@QAE@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@12@@Z
+    ??0?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@__Cr@std@@QAE@XZ
     ??0?$__non_trivial_if@$00V?$allocator@PAPBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@@__Cr@std@@QAE@XZ
     ??0?$__non_trivial_if@$00V?$allocator@PAU?$__hash_node_base@PAU?$__hash_node@PBUCordRep@cord_internal@absl@@PAX@__Cr@std@@@__Cr@std@@@__Cr@std@@@__Cr@std@@QAE@XZ
     ??0?$__non_trivial_if@$00V?$allocator@PAU?$__hash_node_base@PAU?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PAX@__Cr@std@@@__Cr@std@@@__Cr@std@@@__Cr@std@@QAE@XZ
@@ -2701,8 +2706,8 @@
     ??R?$__bucket_list_deallocator@V?$allocator@PAU?$__hash_node_base@PAU?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PAX@__Cr@std@@@__Cr@std@@@__Cr@std@@@__Cr@std@@QAEXPAPAU?$__hash_node_base@PAU?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PAX@__Cr@std@@@12@@Z
     ??R?$__hash_node_destructor@V?$allocator@U?$__hash_node@PBUCordRep@cord_internal@absl@@PAX@__Cr@std@@@__Cr@std@@@__Cr@std@@QAEXPAU?$__hash_node@PBUCordRep@cord_internal@absl@@PAX@12@@Z
     ??R?$__hash_node_destructor@V?$allocator@U?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PAX@__Cr@std@@@__Cr@std@@@__Cr@std@@QAEXPAU?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@PAX@12@@Z
-    ??R?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@X@__Cr@std@@QBE@ABV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@12@ABU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@12@@Z
-    ??R?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@X@__Cr@std@@QBE@ABV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@12@ABU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@12@@Z
+    ??R?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@X@__Cr@std@@QBE@ABV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@12@ABU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@12@@Z
+    ??R?$__lazy_synth_three_way_comparator@V?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@__Cr@std@@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@X@__Cr@std@@QBE@ABV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@12@ABU?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@12@@Z
     ??R?$__policy_func@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@__Cr@std@@@__Cr@std@@ABV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@@Z@__function@__Cr@std@@QBE?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@__Cr@std@@@23@ABV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@23@@Z
     ??R?$__policy_func@$$A6AXAAVCommandLineFlag@absl@@@Z@__function@__Cr@std@@QBEXAAVCommandLineFlag@absl@@@Z
     ??R?$__policy_func@$$A6A_NABVCommandLineFlag@absl@@@Z@__function@__Cr@std@@QBE_NABVCommandLineFlag@absl@@@Z
@@ -5361,8 +5366,8 @@
     ?iterator_at@?$raw_hash_set@U?$FlatHashMapPolicy@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PAVCommandLineFlag@absl@@@container_internal@absl@@UStringHash@23@UStringEq@23@V?$allocator@U?$pair@$$CBV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PAVCommandLineFlag@absl@@@__Cr@std@@@__Cr@std@@@container_internal@absl@@IAE?AViterator@123@I@Z
     ?iterator_at_ptr@?$raw_hash_set@U?$FlatHashMapPolicy@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PAVCommandLineFlag@absl@@@container_internal@absl@@UStringHash@23@UStringEq@23@V?$allocator@U?$pair@$$CBV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PAVCommandLineFlag@absl@@@__Cr@std@@@__Cr@std@@@container_internal@absl@@IAE?AViterator@123@U?$pair@PAW4ctrl_t@container_internal@absl@@PAX@__Cr@std@@@Z
     ?kDefaultIterControl@container_internal@absl@@3W4ctrl_t@12@A
-    ?key_comp@?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@QBEABU?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@XZ
-    ?key_comp@?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@__Cr@std@@QBEABU?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@XZ
+    ?key_comp@?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$map@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@U?$less@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@__Cr@std@@@23@@23@@23@U?$less@X@23@@__Cr@std@@QBEABU?$less@X@23@XZ
+    ?key_comp@?$__map_value_compare@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@V?$vector@PBVCommandLineFlag@absl@@V?$allocator@PBVCommandLineFlag@absl@@@__Cr@std@@@23@@23@U?$less@X@23@@__Cr@std@@QBEABU?$less@X@23@XZ
     ?key_eq@?$__hash_table@PBUCordRep@cord_internal@absl@@U?$hash@PBUCordRep@cord_internal@absl@@@__Cr@std@@U?$equal_to@PBUCordRep@cord_internal@absl@@@56@V?$allocator@PBUCordRep@cord_internal@absl@@@56@@__Cr@std@@QAEAAU?$equal_to@PBUCordRep@cord_internal@absl@@@23@XZ
     ?key_eq@?$__hash_table@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@V?$__unordered_map_hasher@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@V?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@__Cr@std@@@23@@__Cr@std@@QAEAAV?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@PBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@23@@23@XZ
     ?length@CordRepBtreeReader@cord_internal@absl@@QBEIXZ
diff --git a/third_party/android_deps/autorolled/VERSION.txt b/third_party/android_deps/autorolled/VERSION.txt
index 19e877f..a4a5d57 100644
--- a/third_party/android_deps/autorolled/VERSION.txt
+++ b/third_party/android_deps/autorolled/VERSION.txt
@@ -1 +1 @@
-cb86445f7390869.8a3917f748a20a7
\ No newline at end of file
+d8ea01e3e4395aa.4026501ef16c4df
\ No newline at end of file
diff --git a/third_party/android_deps/autorolled/bill_of_materials.json b/third_party/android_deps/autorolled/bill_of_materials.json
index 066d01f1..0cf69b4 100644
--- a/third_party/android_deps/autorolled/bill_of_materials.json
+++ b/third_party/android_deps/autorolled/bill_of_materials.json
@@ -1832,17 +1832,17 @@
     {
         "name": "asm",
         "group": "org.ow2.asm",
-        "version": "9.8"
+        "version": "9.9"
     },
     {
         "name": "asm-commons",
         "group": "org.ow2.asm",
-        "version": "9.8"
+        "version": "9.9"
     },
     {
         "name": "asm-tree",
         "group": "org.ow2.asm",
-        "version": "9.8"
+        "version": "9.9"
     },
     {
         "name": "reactive-streams",
diff --git a/third_party/android_deps/autorolled/build.gradle b/third_party/android_deps/autorolled/build.gradle
index cd32a1a..e4d45b3 100644
--- a/third_party/android_deps/autorolled/build.gradle
+++ b/third_party/android_deps/autorolled/build.gradle
@@ -380,9 +380,9 @@
 versionCache['org.mockito:mockito-core'] = '5.19.0'
 versionCache['org.mockito:mockito-subclass'] = '5.19.0'
 versionCache['org.objenesis:objenesis'] = '3.3'
-versionCache['org.ow2.asm:asm'] = '9.8'
-versionCache['org.ow2.asm:asm-commons'] = '9.8'
-versionCache['org.ow2.asm:asm-tree'] = '9.8'
+versionCache['org.ow2.asm:asm'] = '9.9'
+versionCache['org.ow2.asm:asm-commons'] = '9.9'
+versionCache['org.ow2.asm:asm-tree'] = '9.9'
 versionCache['org.reactivestreams:reactive-streams'] = '1.0.4'
 versionCache['org.robolectric:annotations'] = '4.16'
 versionCache['org.robolectric:junit'] = '4.16'
diff --git a/third_party/android_deps/autorolled/committed/libs/org_ow2_asm_asm/README.chromium b/third_party/android_deps/autorolled/committed/libs/org_ow2_asm_asm/README.chromium
index 1f42b65..9beb22e 100644
--- a/third_party/android_deps/autorolled/committed/libs/org_ow2_asm_asm/README.chromium
+++ b/third_party/android_deps/autorolled/committed/libs/org_ow2_asm_asm/README.chromium
@@ -1,7 +1,7 @@
 Name: asm
 Short Name: asm
-URL: https://repo.maven.apache.org/maven2/org/ow2/asm/asm/9.8/asm-9.8.jar
-Version: 9.8
+URL: https://repo.maven.apache.org/maven2/org/ow2/asm/asm/9.9/asm-9.9.jar
+Version: 9.9
 Update Mechanism: Autoroll
 License: BSD-3-Clause
 License File: LICENSE
diff --git a/third_party/android_deps/autorolled/committed/libs/org_ow2_asm_asm_commons/README.chromium b/third_party/android_deps/autorolled/committed/libs/org_ow2_asm_asm_commons/README.chromium
index 7b2383f..6d75e45 100644
--- a/third_party/android_deps/autorolled/committed/libs/org_ow2_asm_asm_commons/README.chromium
+++ b/third_party/android_deps/autorolled/committed/libs/org_ow2_asm_asm_commons/README.chromium
@@ -1,7 +1,7 @@
 Name: asm-commons
 Short Name: asm-commons
-URL: https://repo.maven.apache.org/maven2/org/ow2/asm/asm-commons/9.8/asm-commons-9.8.jar
-Version: 9.8
+URL: https://repo.maven.apache.org/maven2/org/ow2/asm/asm-commons/9.9/asm-commons-9.9.jar
+Version: 9.9
 Update Mechanism: Autoroll
 License: BSD-3-Clause
 License File: LICENSE
diff --git a/third_party/android_deps/autorolled/committed/libs/org_ow2_asm_asm_tree/README.chromium b/third_party/android_deps/autorolled/committed/libs/org_ow2_asm_asm_tree/README.chromium
index 77cb5cb..ce621e17 100644
--- a/third_party/android_deps/autorolled/committed/libs/org_ow2_asm_asm_tree/README.chromium
+++ b/third_party/android_deps/autorolled/committed/libs/org_ow2_asm_asm_tree/README.chromium
@@ -1,7 +1,7 @@
 Name: asm-tree
 Short Name: asm-tree
-URL: https://repo.maven.apache.org/maven2/org/ow2/asm/asm-tree/9.8/asm-tree-9.8.jar
-Version: 9.8
+URL: https://repo.maven.apache.org/maven2/org/ow2/asm/asm-tree/9.9/asm-tree-9.9.jar
+Version: 9.9
 Update Mechanism: Autoroll
 License: BSD-3-Clause
 License File: LICENSE
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle
index bd4099c..7d374e4f 100644
--- a/third_party/androidx/build.gradle
+++ b/third_party/androidx/build.gradle
@@ -314,7 +314,7 @@
     google()
     maven {
         // This URL is generated by the fetch_all_androidx.py script.
-        url 'https://androidx.dev/snapshots/builds/14214693/artifacts/repository'
+        url 'https://androidx.dev/snapshots/builds/14224118/artifacts/repository'
     }
     mavenCentral()
 }
diff --git a/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium b/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium
index d5e4ce2..e5ff39a3 100644
--- a/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium
@@ -1,6 +1,6 @@
 Name: Activity
 Short Name: activity
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/activity/activity/1.12.0-SNAPSHOT/activity-1.12.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/activity/activity/1.12.0-SNAPSHOT/activity-1.12.0-20251007.062658-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium b/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium
index 6cde9b1cc..b4c63acc 100644
--- a/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium
@@ -1,6 +1,6 @@
 Name: Activity Compose
 Short Name: activity-compose
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/activity/activity-compose/1.12.0-SNAPSHOT/activity-compose-1.12.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/activity/activity-compose/1.12.0-SNAPSHOT/activity-compose-1.12.0-20251007.062658-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium
index 295b8df..9a2be24 100644
--- a/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Activity Kotlin Extensions
 Short Name: activity-ktx
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/activity/activity-ktx/1.12.0-SNAPSHOT/activity-ktx-1.12.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/activity/activity-ktx/1.12.0-SNAPSHOT/activity-ktx-1.12.0-20251007.062658-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium b/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium
index b9c7c88..a62dfe76 100644
--- a/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium
@@ -1,6 +1,6 @@
 Name: Experimental annotation
 Short Name: annotation-experimental
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/annotation/annotation-experimental/1.6.0-SNAPSHOT/annotation-experimental-1.6.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/annotation/annotation-experimental/1.6.0-SNAPSHOT/annotation-experimental-1.6.0-20251007.062658-1.aar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_annotation_annotation_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_annotation_annotation_jvm/README.chromium
index 3b60b7ee..e702eba2 100644
--- a/third_party/androidx/committed/libs/androidx_annotation_annotation_jvm/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_annotation_annotation_jvm/README.chromium
@@ -1,6 +1,6 @@
 Name: Annotation
 Short Name: annotation-jvm
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/annotation/annotation-jvm/1.10.0-SNAPSHOT/annotation-jvm-1.10.0-20251004.032114-1.jar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/annotation/annotation-jvm/1.10.0-SNAPSHOT/annotation-jvm-1.10.0-20251007.062658-1.jar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium b/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium
index 637b2f9..905dd6f 100644
--- a/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium
@@ -1,6 +1,6 @@
 Name: AppCompat
 Short Name: appcompat
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/appcompat/appcompat/1.8.0-SNAPSHOT/appcompat-1.8.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/appcompat/appcompat/1.8.0-SNAPSHOT/appcompat-1.8.0-20251007.062658-1.aar
 Version: 1.8.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium b/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium
index 55c010b..cd3a6b1 100644
--- a/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium
@@ -1,6 +1,6 @@
 Name: AppCompat Resources
 Short Name: appcompat-resources
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/appcompat/appcompat-resources/1.8.0-SNAPSHOT/appcompat-resources-1.8.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/appcompat/appcompat-resources/1.8.0-SNAPSHOT/appcompat-resources-1.8.0-20251007.062658-1.aar
 Version: 1.8.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium b/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium
index baebb09..4044efe0 100644
--- a/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium
@@ -1,6 +1,6 @@
 Name: AppSearch
 Short Name: appsearch
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/appsearch/appsearch/1.2.0-SNAPSHOT/appsearch-1.2.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/appsearch/appsearch/1.2.0-SNAPSHOT/appsearch-1.2.0-20251007.062658-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium
index 06393fe..30c2159 100644
--- a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium
@@ -1,6 +1,6 @@
 Name: AppSearch Builtin Types
 Short Name: appsearch-builtin-types
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/appsearch/appsearch-builtin-types/1.2.0-SNAPSHOT/appsearch-builtin-types-1.2.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/appsearch/appsearch-builtin-types/1.2.0-SNAPSHOT/appsearch-builtin-types-1.2.0-20251007.062658-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium
index 717eb67..6e03f95 100644
--- a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium
@@ -1,6 +1,6 @@
 Name: AppSearch Platform Storage
 Short Name: appsearch-platform-storage
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/appsearch/appsearch-platform-storage/1.2.0-SNAPSHOT/appsearch-platform-storage-1.2.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/appsearch/appsearch-platform-storage/1.2.0-SNAPSHOT/appsearch-platform-storage-1.2.0-20251007.062658-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium b/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium
index 4102357..197a7832 100644
--- a/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium
@@ -1,6 +1,6 @@
 Name: Arch-Common
 Short Name: core-common
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/arch/core/core-common/2.3.0-SNAPSHOT/core-common-2.3.0-20251004.032114-1.jar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/arch/core/core-common/2.3.0-SNAPSHOT/core-common-2.3.0-20251007.062658-1.jar
 Version: 2.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium b/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium
index 9f588f8..9caf9d48 100644
--- a/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium
@@ -1,6 +1,6 @@
 Name: Arch-Runtime
 Short Name: core-runtime
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/arch/core/core-runtime/2.3.0-SNAPSHOT/core-runtime-2.3.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/arch/core/core-runtime/2.3.0-SNAPSHOT/core-runtime-2.3.0-20251007.062658-1.aar
 Version: 2.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium b/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium
index 8c3ead0..48bd52e 100644
--- a/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium
@@ -1,6 +1,6 @@
 Name: Autofill
 Short Name: autofill
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/autofill/autofill/1.4.0-SNAPSHOT/autofill-1.4.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/autofill/autofill/1.4.0-SNAPSHOT/autofill-1.4.0-20251007.062658-1.aar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium
index b605e51..ce0b218 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark - Common
 Short Name: benchmark-common
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/benchmark/benchmark-common/1.5.0-SNAPSHOT/benchmark-common-1.5.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/benchmark/benchmark-common/1.5.0-SNAPSHOT/benchmark-common-1.5.0-20251007.062658-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium
index eda7d12..74561ad 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark - JUnit4
 Short Name: benchmark-junit4
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/benchmark/benchmark-junit4/1.5.0-SNAPSHOT/benchmark-junit4-1.5.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/benchmark/benchmark-junit4/1.5.0-SNAPSHOT/benchmark-junit4-1.5.0-20251007.062658-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium
index a4a9151..0d49afa 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark - Macrobenchmark
 Short Name: benchmark-macro
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/benchmark/benchmark-macro/1.5.0-SNAPSHOT/benchmark-macro-1.5.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/benchmark/benchmark-macro/1.5.0-SNAPSHOT/benchmark-macro-1.5.0-20251007.062658-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium
index 3f8003d..d2f40a9 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark - Macrobenchmark JUnit4
 Short Name: benchmark-macro-junit4
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/benchmark/benchmark-macro-junit4/1.5.0-SNAPSHOT/benchmark-macro-junit4-1.5.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/benchmark/benchmark-macro-junit4/1.5.0-SNAPSHOT/benchmark-macro-junit4-1.5.0-20251007.062658-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium
index f5a40eaf..4b53de5a 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark TraceProcessor
 Short Name: benchmark-traceprocessor-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/benchmark/benchmark-traceprocessor-android/1.5.0-SNAPSHOT/benchmark-traceprocessor-android-1.5.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/benchmark/benchmark-traceprocessor-android/1.5.0-SNAPSHOT/benchmark-traceprocessor-android-1.5.0-20251007.062658-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium b/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium
index ac7e7bc..a5356da 100644
--- a/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium
@@ -1,6 +1,6 @@
 Name: Biometric
 Short Name: biometric
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/biometric/biometric/1.4.0-SNAPSHOT/biometric-1.4.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/biometric/biometric/1.4.0-SNAPSHOT/biometric-1.4.0-20251007.062658-1.aar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_browser_browser/README.chromium b/third_party/androidx/committed/libs/androidx_browser_browser/README.chromium
index 9380556..2a08a92 100644
--- a/third_party/androidx/committed/libs/androidx_browser_browser/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_browser_browser/README.chromium
@@ -1,6 +1,6 @@
 Name: Browser
 Short Name: browser
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/browser/browser/1.10.0-SNAPSHOT/browser-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/browser/browser/1.10.0-SNAPSHOT/browser-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium b/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium
index 969f3ff0..07f883cc 100644
--- a/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium
@@ -1,6 +1,6 @@
 Name: CardView
 Short Name: cardview
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/cardview/cardview/1.1.0-SNAPSHOT/cardview-1.1.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/cardview/cardview/1.1.0-SNAPSHOT/cardview-1.1.0-20251007.062658-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium
index 9de0344f..c213597 100644
--- a/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium
@@ -1,6 +1,6 @@
 Name: collections
 Short Name: collection-jvm
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/collection/collection-jvm/1.6.0-SNAPSHOT/collection-jvm-1.6.0-20251004.032114-1.jar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/collection/collection-jvm/1.6.0-SNAPSHOT/collection-jvm-1.6.0-20251007.062658-1.jar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium
index 31bdf9d..92327f9 100644
--- a/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Collections Kotlin Extensions
 Short Name: collection-ktx
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/collection/collection-ktx/1.6.0-SNAPSHOT/collection-ktx-1.6.0-20251004.032114-1.jar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/collection/collection-ktx/1.6.0-SNAPSHOT/collection-ktx-1.6.0-20251007.062658-1.jar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium
index 558bc9f..b9239e44 100644
--- a/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Animation
 Short Name: animation-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/animation/animation-android/1.10.0-SNAPSHOT/animation-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/animation/animation-android/1.10.0-SNAPSHOT/animation-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium
index 078299be..49b092d 100644
--- a/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Animation Core
 Short Name: animation-core-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/animation/animation-core-android/1.10.0-SNAPSHOT/animation-core-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/animation/animation-core-android/1.10.0-SNAPSHOT/animation-core-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium
index 798d12c..16b5a09 100644
--- a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Foundation
 Short Name: foundation-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/foundation/foundation-android/1.10.0-SNAPSHOT/foundation-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/foundation/foundation-android/1.10.0-SNAPSHOT/foundation-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium
index b067f33..c1facd7e 100644
--- a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Layouts
 Short Name: foundation-layout-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/foundation/foundation-layout-android/1.10.0-SNAPSHOT/foundation-layout-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/foundation/foundation-layout-android/1.10.0-SNAPSHOT/foundation-layout-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium
index a3110cd1..c88519e 100644
--- a/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Material3 Components
 Short Name: material3-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/material3/material3-android/1.5.0-SNAPSHOT/material3-android-1.5.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/material3/material3-android/1.5.0-SNAPSHOT/material3-android-1.5.0-20251007.062658-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium
index 7e8f2a8..07200c2 100644
--- a/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Material Ripple
 Short Name: material-ripple-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/material/material-ripple-android/1.10.0-SNAPSHOT/material-ripple-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/material/material-ripple-android/1.10.0-SNAPSHOT/material-ripple-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium
index 74a54af..a86df97 100644
--- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Runtime
 Short Name: runtime-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/runtime/runtime-android/1.10.0-SNAPSHOT/runtime-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/runtime/runtime-android/1.10.0-SNAPSHOT/runtime-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium
index d4e5ca9..ae5f80a 100644
--- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Runtime Annotation
 Short Name: runtime-annotation-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/runtime/runtime-annotation-android/1.10.0-SNAPSHOT/runtime-annotation-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/runtime/runtime-annotation-android/1.10.0-SNAPSHOT/runtime-annotation-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium
index cc99e61..836153e 100644
--- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Runtime Retain
 Short Name: runtime-retain-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/runtime/runtime-retain-android/1.10.0-SNAPSHOT/runtime-retain-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/runtime/runtime-retain-android/1.10.0-SNAPSHOT/runtime-retain-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium
index 6711349..c0700de 100644
--- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Saveable
 Short Name: runtime-saveable-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/runtime/runtime-saveable-android/1.10.0-SNAPSHOT/runtime-saveable-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/runtime/runtime-saveable-android/1.10.0-SNAPSHOT/runtime-saveable-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium
index 2105fe1..aa8f103 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose UI
 Short Name: ui-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/ui/ui-android/1.10.0-SNAPSHOT/ui-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/ui/ui-android/1.10.0-SNAPSHOT/ui-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium
index bd9d292..a675ed9 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Geometry
 Short Name: ui-geometry-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/ui/ui-geometry-android/1.10.0-SNAPSHOT/ui-geometry-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/ui/ui-geometry-android/1.10.0-SNAPSHOT/ui-geometry-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium
index dcea12e..be32f6e 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Graphics
 Short Name: ui-graphics-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/ui/ui-graphics-android/1.10.0-SNAPSHOT/ui-graphics-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/ui/ui-graphics-android/1.10.0-SNAPSHOT/ui-graphics-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium
index 2481a3b..316270e 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Testing
 Short Name: ui-test-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/ui/ui-test-android/1.10.0-SNAPSHOT/ui-test-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/ui/ui-test-android/1.10.0-SNAPSHOT/ui-test-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium
index 320c56931..d8d0e13 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Testing for JUnit4
 Short Name: ui-test-junit4-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/ui/ui-test-junit4-android/1.10.0-SNAPSHOT/ui-test-junit4-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/ui/ui-test-junit4-android/1.10.0-SNAPSHOT/ui-test-junit4-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium
index 03f8221c..c75408b 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Testing manifest dependency
 Short Name: ui-test-manifest
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/ui/ui-test-manifest/1.10.0-SNAPSHOT/ui-test-manifest-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/ui/ui-test-manifest/1.10.0-SNAPSHOT/ui-test-manifest-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium
index d59f290..e8e7eef 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose UI Text
 Short Name: ui-text-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/ui/ui-text-android/1.10.0-SNAPSHOT/ui-text-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/ui/ui-text-android/1.10.0-SNAPSHOT/ui-text-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium
index f2612af..9752cdef 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Google Fonts integration
 Short Name: ui-text-google-fonts
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/ui/ui-text-google-fonts/1.10.0-SNAPSHOT/ui-text-google-fonts-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/ui/ui-text-google-fonts/1.10.0-SNAPSHOT/ui-text-google-fonts-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium
index 71deb8c53..267ddd40 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Unit
 Short Name: ui-unit-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/ui/ui-unit-android/1.10.0-SNAPSHOT/ui-unit-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/ui/ui-unit-android/1.10.0-SNAPSHOT/ui-unit-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium
index 58f54e3..4580db3 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Util
 Short Name: ui-util-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/compose/ui/ui-util-android/1.10.0-SNAPSHOT/ui-util-android-1.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/compose/ui/ui-util-android/1.10.0-SNAPSHOT/ui-util-android-1.10.0-20251007.062658-1.aar
 Version: 1.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium
index 02a718b..2e52f63 100644
--- a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium
@@ -1,6 +1,6 @@
 Name: ConstraintLayout
 Short Name: constraintlayout
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/constraintlayout/constraintlayout/2.3.0-SNAPSHOT/constraintlayout-2.3.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/constraintlayout/constraintlayout/2.3.0-SNAPSHOT/constraintlayout-2.3.0-20251007.062658-1.aar
 Version: 2.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium
index d36fc3c..e17e29b8 100644
--- a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium
@@ -1,6 +1,6 @@
 Name: ConstraintLayout Core
 Short Name: constraintlayout-core
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/constraintlayout/constraintlayout-core/1.2.0-SNAPSHOT/constraintlayout-core-1.2.0-20251004.032114-1.jar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/constraintlayout/constraintlayout-core/1.2.0-SNAPSHOT/constraintlayout-core-1.2.0-20251007.062658-1.jar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core/README.chromium b/third_party/androidx/committed/libs/androidx_core_core/README.chromium
index 6bff531..04c032a 100644
--- a/third_party/androidx/committed/libs/androidx_core_core/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_core_core/README.chromium
@@ -1,6 +1,6 @@
 Name: Core
 Short Name: core
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/core/core/1.18.0-SNAPSHOT/core-1.18.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/core/core/1.18.0-SNAPSHOT/core-1.18.0-20251007.062658-1.aar
 Version: 1.18.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium
index c60feb45..ba359c18 100644
--- a/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Core Kotlin Extensions
 Short Name: core-ktx
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/core/core-ktx/1.18.0-SNAPSHOT/core-ktx-1.18.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/core/core-ktx/1.18.0-SNAPSHOT/core-ktx-1.18.0-20251007.062658-1.aar
 Version: 1.18.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium b/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium
index 4dfe0b4..7c99459 100644
--- a/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.core:core-viewtree
 Short Name: core-viewtree
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/core/core-viewtree/1.1.0-SNAPSHOT/core-viewtree-1.1.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/core/core-viewtree/1.1.0-SNAPSHOT/core-viewtree-1.1.0-20251007.062658-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium
index 82c83c0..945cdb5 100644
--- a/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium
@@ -1,6 +1,6 @@
 Name: Credentials
 Short Name: credentials
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/credentials/credentials/1.6.0-SNAPSHOT/credentials-1.6.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/credentials/credentials/1.6.0-SNAPSHOT/credentials-1.6.0-20251007.062658-1.aar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium
index 920c42a4..7f40664 100644
--- a/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium
@@ -1,6 +1,6 @@
 Name: Credentials Play Services Auth
 Short Name: credentials-play-services-auth
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/credentials/credentials-play-services-auth/1.6.0-SNAPSHOT/credentials-play-services-auth-1.6.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/credentials/credentials-play-services-auth/1.6.0-SNAPSHOT/credentials-play-services-auth-1.6.0-20251007.062658-1.aar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium
index 6d97f50..502595d0 100644
--- a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.credentials.registry:registry-provider
 Short Name: registry-provider
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/credentials/registry/registry-provider/1.0.0-SNAPSHOT/registry-provider-1.0.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/credentials/registry/registry-provider/1.0.0-SNAPSHOT/registry-provider-1.0.0-20251007.062658-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium
index 7aaadb0..749011dd 100644
--- a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.credentials.registry:registry-provider-play-services
 Short Name: registry-provider-play-services
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/credentials/registry/registry-provider-play-services/1.0.0-SNAPSHOT/registry-provider-play-services-1.0.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/credentials/registry/registry-provider-play-services/1.0.0-SNAPSHOT/registry-provider-play-services-1.0.0-20251007.062658-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium b/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium
index de4bd433..06d7a39 100644
--- a/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium
@@ -1,6 +1,6 @@
 Name: Cursor Adapter
 Short Name: cursoradapter
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/cursoradapter/cursoradapter/1.1.0-SNAPSHOT/cursoradapter-1.1.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/cursoradapter/cursoradapter/1.1.0-SNAPSHOT/cursoradapter-1.1.0-20251007.062658-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium
index bd067ce..c27ddd9 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium
@@ -1,6 +1,6 @@
 Name: DataStore
 Short Name: datastore-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/datastore/datastore-android/1.2.0-SNAPSHOT/datastore-android-1.2.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/datastore/datastore-android/1.2.0-SNAPSHOT/datastore-android-1.2.0-20251007.062658-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium
index db3d3702..8e83bd3 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium
@@ -1,6 +1,6 @@
 Name: DataStore Core
 Short Name: datastore-core-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/datastore/datastore-core-android/1.2.0-SNAPSHOT/datastore-core-android-1.2.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/datastore/datastore-core-android/1.2.0-SNAPSHOT/datastore-core-android-1.2.0-20251007.062658-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium
index c1d5f5c..c4f7d57 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium
@@ -1,6 +1,6 @@
 Name: DataStore Core Okio
 Short Name: datastore-core-okio-jvm
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/datastore/datastore-core-okio-jvm/1.2.0-SNAPSHOT/datastore-core-okio-jvm-1.2.0-20251004.032114-1.jar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/datastore/datastore-core-okio-jvm/1.2.0-SNAPSHOT/datastore-core-okio-jvm-1.2.0-20251007.062658-1.jar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium
index 15400787..e502117 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Preferences DataStore
 Short Name: datastore-preferences-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/datastore/datastore-preferences-android/1.2.0-SNAPSHOT/datastore-preferences-android-1.2.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/datastore/datastore-preferences-android/1.2.0-SNAPSHOT/datastore-preferences-android-1.2.0-20251007.062658-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium
index d087815..5106265 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Preferences DataStore Core
 Short Name: datastore-preferences-core-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/datastore/datastore-preferences-core-android/1.2.0-SNAPSHOT/datastore-preferences-core-android-1.2.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/datastore/datastore-preferences-core-android/1.2.0-SNAPSHOT/datastore-preferences-core-android-1.2.0-20251007.062658-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium
index 3d18bed4..e819e54 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium
@@ -1,6 +1,6 @@
 Name: Preferences External Protobuf
 Short Name: datastore-preferences-external-protobuf
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/datastore/datastore-preferences-external-protobuf/1.2.0-SNAPSHOT/datastore-preferences-external-protobuf-1.2.0-20251004.032114-1.jar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/datastore/datastore-preferences-external-protobuf/1.2.0-SNAPSHOT/datastore-preferences-external-protobuf-1.2.0-20251007.062658-1.jar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: BSD-3-Clause
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium
index 1dbab09..54a33dd 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium
@@ -1,6 +1,6 @@
 Name: Preferences DataStore Proto
 Short Name: datastore-preferences-proto
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/datastore/datastore-preferences-proto/1.2.0-SNAPSHOT/datastore-preferences-proto-1.2.0-20251004.032114-1.jar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/datastore/datastore-preferences-proto/1.2.0-SNAPSHOT/datastore-preferences-proto-1.2.0-20251007.062658-1.jar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium b/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium
index 12f0692..a1a4d30 100644
--- a/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium
@@ -1,6 +1,6 @@
 Name: Drawer Layout
 Short Name: drawerlayout
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/drawerlayout/drawerlayout/1.3.0-SNAPSHOT/drawerlayout-1.3.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/drawerlayout/drawerlayout/1.3.0-SNAPSHOT/drawerlayout-1.3.0-20251007.062658-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_emoji_emoji/README.chromium b/third_party/androidx/committed/libs/androidx_emoji_emoji/README.chromium
index 060aee4..1f23eeff 100644
--- a/third_party/androidx/committed/libs/androidx_emoji_emoji/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_emoji_emoji/README.chromium
@@ -1,6 +1,6 @@
 Name: Emoji
 Short Name: emoji
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/emoji/emoji/1.2.0-SNAPSHOT/emoji-1.2.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/emoji/emoji/1.2.0-SNAPSHOT/emoji-1.2.0-20251007.062658-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0, SIL Open Font License, Version 1.1, Unicode, Inc. License
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium
index 03767df..1d9f2c68 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium
@@ -1,6 +1,6 @@
 Name: fragment
 Short Name: fragment
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/fragment/fragment/1.9.0-SNAPSHOT/fragment-1.9.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/fragment/fragment/1.9.0-SNAPSHOT/fragment-1.9.0-20251007.062658-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium
index dac85fd..fa0623a9 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium
@@ -1,6 +1,6 @@
 Name: Fragment Compose
 Short Name: fragment-compose
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/fragment/fragment-compose/1.9.0-SNAPSHOT/fragment-compose-1.9.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/fragment/fragment-compose/1.9.0-SNAPSHOT/fragment-compose-1.9.0-20251007.062658-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium
index 83239ef7..668035c 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Fragment Kotlin Extensions
 Short Name: fragment-ktx
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/fragment/fragment-ktx/1.9.0-SNAPSHOT/fragment-ktx-1.9.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/fragment/fragment-ktx/1.9.0-SNAPSHOT/fragment-ktx-1.9.0-20251007.062658-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium
index 6719655ed..ebdacbd 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium
@@ -1,6 +1,6 @@
 Name: Fragment Testing Extensions
 Short Name: fragment-testing
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/fragment/fragment-testing/1.9.0-SNAPSHOT/fragment-testing-1.9.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/fragment/fragment-testing/1.9.0-SNAPSHOT/fragment-testing-1.9.0-20251007.062658-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium
index ba01a13..dded67d 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium
@@ -1,6 +1,6 @@
 Name: Fragment Testing Manifest dependency
 Short Name: fragment-testing-manifest
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/fragment/fragment-testing-manifest/1.9.0-SNAPSHOT/fragment-testing-manifest-1.9.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/fragment/fragment-testing-manifest/1.9.0-SNAPSHOT/fragment-testing-manifest-1.9.0-20251007.062658-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium b/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium
index 3a22641..8cb5472 100644
--- a/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium
@@ -1,6 +1,6 @@
 Name: Android Graphics Path
 Short Name: graphics-path
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/graphics/graphics-path/1.1.0-SNAPSHOT/graphics-path-1.1.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/graphics/graphics-path/1.1.0-SNAPSHOT/graphics-path-1.1.0-20251007.062658-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_graphics_graphics_shapes_android/README.chromium b/third_party/androidx/committed/libs/androidx_graphics_graphics_shapes_android/README.chromium
index 980cc7c..4815da3 100644
--- a/third_party/androidx/committed/libs/androidx_graphics_graphics_shapes_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_graphics_graphics_shapes_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Graphics Shapes
 Short Name: graphics-shapes-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/graphics/graphics-shapes-android/1.1.0-SNAPSHOT/graphics-shapes-android-1.1.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/graphics/graphics-shapes-android/1.1.0-SNAPSHOT/graphics-shapes-android-1.1.0-20251007.062658-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium b/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium
index c4cc7e2..6af5795 100644
--- a/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium
@@ -1,6 +1,6 @@
 Name: Interpolators
 Short Name: interpolator
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/interpolator/interpolator/1.1.0-SNAPSHOT/interpolator-1.1.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/interpolator/interpolator/1.1.0-SNAPSHOT/interpolator-1.1.0-20251007.062658-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium
index 77d6565..840e613f 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle-Common for Java 8
 Short Name: lifecycle-common-java8
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-common-java8/2.10.0-SNAPSHOT/lifecycle-common-java8-2.10.0-20251004.032114-1.jar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-common-java8/2.10.0-SNAPSHOT/lifecycle-common-java8-2.10.0-20251007.062658-1.jar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium
index 5e7d4f4..65c6183 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle-Common
 Short Name: lifecycle-common-jvm
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-common-jvm/2.10.0-SNAPSHOT/lifecycle-common-jvm-2.10.0-20251004.032114-1.jar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-common-jvm/2.10.0-SNAPSHOT/lifecycle-common-jvm-2.10.0-20251007.062658-1.jar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium
index fb9e89e..e8614ba 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle LiveData
 Short Name: lifecycle-livedata
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-livedata/2.10.0-SNAPSHOT/lifecycle-livedata-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-livedata/2.10.0-SNAPSHOT/lifecycle-livedata-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium
index 273e512..6ec591f 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle LiveData Core
 Short Name: lifecycle-livedata-core
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core/2.10.0-SNAPSHOT/lifecycle-livedata-core-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core/2.10.0-SNAPSHOT/lifecycle-livedata-core-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium
index d08360c..bff0a9a 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: LiveData Core Kotlin Extensions
 Short Name: lifecycle-livedata-core-ktx
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core-ktx/2.10.0-SNAPSHOT/lifecycle-livedata-core-ktx-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core-ktx/2.10.0-SNAPSHOT/lifecycle-livedata-core-ktx-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium
index b5e52994..91fc4e3 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: LiveData Kotlin Extensions
 Short Name: lifecycle-livedata-ktx
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-livedata-ktx/2.10.0-SNAPSHOT/lifecycle-livedata-ktx-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-livedata-ktx/2.10.0-SNAPSHOT/lifecycle-livedata-ktx-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium
index 8b10644..3285418 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Process
 Short Name: lifecycle-process
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-process/2.10.0-SNAPSHOT/lifecycle-process-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-process/2.10.0-SNAPSHOT/lifecycle-process-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium
index 053df620..181539a 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Runtime
 Short Name: lifecycle-runtime-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-runtime-android/2.10.0-SNAPSHOT/lifecycle-runtime-android-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-runtime-android/2.10.0-SNAPSHOT/lifecycle-runtime-android-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium
index c76ff24..ec7104ea 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Runtime Compose
 Short Name: lifecycle-runtime-compose-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-runtime-compose-android/2.10.0-SNAPSHOT/lifecycle-runtime-compose-android-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-runtime-compose-android/2.10.0-SNAPSHOT/lifecycle-runtime-compose-android-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium
index c83110a3..00dd676 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Kotlin Extensions
 Short Name: lifecycle-runtime-ktx-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-runtime-ktx-android/2.10.0-SNAPSHOT/lifecycle-runtime-ktx-android-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-runtime-ktx-android/2.10.0-SNAPSHOT/lifecycle-runtime-ktx-android-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium
index 19289b2..a3eaad6 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Service
 Short Name: lifecycle-service
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-service/2.10.0-SNAPSHOT/lifecycle-service-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-service/2.10.0-SNAPSHOT/lifecycle-service-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium
index 6b9e80d..ba133b6 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle ViewModel
 Short Name: lifecycle-viewmodel-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-android/2.10.0-SNAPSHOT/lifecycle-viewmodel-android-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-android/2.10.0-SNAPSHOT/lifecycle-viewmodel-android-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium
index 19ea16f1..ff3fdcb0 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle ViewModel Compose
 Short Name: lifecycle-viewmodel-compose-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-compose-android/2.10.0-SNAPSHOT/lifecycle-viewmodel-compose-android-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-compose-android/2.10.0-SNAPSHOT/lifecycle-viewmodel-compose-android-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium
index 808c8cd..e99e439 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle ViewModel Kotlin Extensions
 Short Name: lifecycle-viewmodel-ktx
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-ktx/2.10.0-SNAPSHOT/lifecycle-viewmodel-ktx-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-ktx/2.10.0-SNAPSHOT/lifecycle-viewmodel-ktx-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium
index 4d49abf..7bb3036 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle ViewModel with SavedState
 Short Name: lifecycle-viewmodel-savedstate-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-savedstate-android/2.10.0-SNAPSHOT/lifecycle-viewmodel-savedstate-android-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-savedstate-android/2.10.0-SNAPSHOT/lifecycle-viewmodel-savedstate-android-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium b/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium
index b701cb5..8a5232b 100644
--- a/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium
@@ -1,6 +1,6 @@
 Name: loader
 Short Name: loader
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/loader/loader/1.2.0-SNAPSHOT/loader-1.2.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/loader/loader/1.2.0-SNAPSHOT/loader-1.2.0-20251007.062658-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_media_media/README.chromium b/third_party/androidx/committed/libs/androidx_media_media/README.chromium
index c3cd612..69fbbbed 100644
--- a/third_party/androidx/committed/libs/androidx_media_media/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_media_media/README.chromium
@@ -1,6 +1,6 @@
 Name: Media
 Short Name: media
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/media/media/1.8.0-SNAPSHOT/media-1.8.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/media/media/1.8.0-SNAPSHOT/media-1.8.0-20251007.062658-1.aar
 Version: 1.8.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium
index fe1efe9..09a300e 100644
--- a/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Navigation Common
 Short Name: navigation-common-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/navigation/navigation-common-android/2.10.0-SNAPSHOT/navigation-common-android-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/navigation/navigation-common-android/2.10.0-SNAPSHOT/navigation-common-android-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium
index 8ef0aff..47211a0 100644
--- a/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Navigation
 Short Name: navigation-compose-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/navigation/navigation-compose-android/2.10.0-SNAPSHOT/navigation-compose-android-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/navigation/navigation-compose-android/2.10.0-SNAPSHOT/navigation-compose-android-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium
index e58b648..c2ff51d 100644
--- a/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Navigation Runtime
 Short Name: navigation-runtime-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/navigation/navigation-runtime-android/2.10.0-SNAPSHOT/navigation-runtime-android-2.10.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/navigation/navigation-runtime-android/2.10.0-SNAPSHOT/navigation-runtime-android-2.10.0-20251007.062658-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium
index f86c9e8..1caba6c7 100644
--- a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Navigation Event
 Short Name: navigationevent-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/navigationevent/navigationevent-android/1.0.0-SNAPSHOT/navigationevent-android-1.0.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/navigationevent/navigationevent-android/1.0.0-SNAPSHOT/navigationevent-android-1.0.0-20251007.062658-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium
index 563cd2a7..d53dca2 100644
--- a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: NavigationEvent Compose
 Short Name: navigationevent-compose-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/navigationevent/navigationevent-compose-android/1.0.0-SNAPSHOT/navigationevent-compose-android-1.0.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/navigationevent/navigationevent-compose-android/1.0.0-SNAPSHOT/navigationevent-compose-android-1.0.0-20251007.062658-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium
index 0577f30..4269ae4 100644
--- a/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Paging-Common
 Short Name: paging-common-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/paging/paging-common-android/3.4.0-SNAPSHOT/paging-common-android-3.4.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/paging/paging-common-android/3.4.0-SNAPSHOT/paging-common-android-3.4.0-20251007.062658-1.aar
 Version: 3.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium
index dacba09f..7d984ed9 100644
--- a/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Paging-Common Kotlin Extensions
 Short Name: paging-common-ktx
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/paging/paging-common-ktx/3.4.0-SNAPSHOT/paging-common-ktx-3.4.0-20251004.032114-1.jar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/paging/paging-common-ktx/3.4.0-SNAPSHOT/paging-common-ktx-3.4.0-20251007.062658-1.jar
 Version: 3.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium
index 6a161a7c..17c42cf 100644
--- a/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Paging-Compose
 Short Name: paging-compose-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/paging/paging-compose-android/3.4.0-SNAPSHOT/paging-compose-android-3.4.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/paging/paging-compose-android/3.4.0-SNAPSHOT/paging-compose-android-3.4.0-20251007.062658-1.aar
 Version: 3.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium
index 399677e..7a4503f 100644
--- a/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium
@@ -1,6 +1,6 @@
 Name: Paging-Runtime
 Short Name: paging-runtime
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/paging/paging-runtime/3.4.0-SNAPSHOT/paging-runtime-3.4.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/paging/paging-runtime/3.4.0-SNAPSHOT/paging-runtime-3.4.0-20251007.062658-1.aar
 Version: 3.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium b/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium
index a24d6161..66f35781 100644
--- a/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium
@@ -1,6 +1,6 @@
 Name: Palette
 Short Name: palette
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/palette/palette/1.1.0-SNAPSHOT/palette-1.1.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/palette/palette/1.1.0-SNAPSHOT/palette-1.1.0-20251007.062658-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/README.chromium b/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/README.chromium
index 831ec72..eba6a6f 100644
--- a/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.pdf:pdf-document-service
 Short Name: pdf-document-service
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/pdf/pdf-document-service/1.0.0-SNAPSHOT/pdf-document-service-1.0.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/pdf/pdf-document-service/1.0.0-SNAPSHOT/pdf-document-service-1.0.0-20251007.062658-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/README.chromium b/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/README.chromium
index 0e0a9c17..591c468 100644
--- a/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.pdf:pdf-viewer
 Short Name: pdf-viewer
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/pdf/pdf-viewer/1.0.0-SNAPSHOT/pdf-viewer-1.0.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/pdf/pdf-viewer/1.0.0-SNAPSHOT/pdf-viewer-1.0.0-20251007.062658-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer_fragment/README.chromium b/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer_fragment/README.chromium
index 8c2fb02..971b704 100644
--- a/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer_fragment/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer_fragment/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.pdf:pdf-viewer-fragment
 Short Name: pdf-viewer-fragment
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/pdf/pdf-viewer-fragment/1.0.0-SNAPSHOT/pdf-viewer-fragment-1.0.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/pdf/pdf-viewer-fragment/1.0.0-SNAPSHOT/pdf-viewer-fragment-1.0.0-20251007.062658-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium b/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium
index 5e973df..de3cfd0 100644
--- a/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium
@@ -1,6 +1,6 @@
 Name: Preference
 Short Name: preference
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/preference/preference/1.3.0-SNAPSHOT/preference-1.3.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/preference/preference/1.3.0-SNAPSHOT/preference-1.3.0-20251007.062658-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium b/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium
index e7045a12..f41a7227 100644
--- a/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium
@@ -1,6 +1,6 @@
 Name: Profile Installer
 Short Name: profileinstaller
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/profileinstaller/profileinstaller/1.5.0-SNAPSHOT/profileinstaller-1.5.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/profileinstaller/profileinstaller/1.5.0-SNAPSHOT/profileinstaller-1.5.0-20251007.062658-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium b/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium
index de79417..2926426 100644
--- a/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium
@@ -1,6 +1,6 @@
 Name: RecyclerView
 Short Name: recyclerview
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/recyclerview/recyclerview/1.5.0-SNAPSHOT/recyclerview-1.5.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/recyclerview/recyclerview/1.5.0-SNAPSHOT/recyclerview-1.5.0-20251007.062658-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium b/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium
index d5f1028..a346397ba 100644
--- a/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium
@@ -1,6 +1,6 @@
 Name: Resource Inspection - Annotations
 Short Name: resourceinspection-annotation
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/resourceinspection/resourceinspection-annotation/1.1.0-SNAPSHOT/resourceinspection-annotation-1.1.0-20251004.032114-1.jar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/resourceinspection/resourceinspection-annotation/1.1.0-SNAPSHOT/resourceinspection-annotation-1.1.0-20251007.062658-1.jar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium
index e6446cd..757fb0f 100644
--- a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Saved State
 Short Name: savedstate-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/savedstate/savedstate-android/1.4.0-SNAPSHOT/savedstate-android-1.4.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/savedstate/savedstate-android/1.4.0-SNAPSHOT/savedstate-android-1.4.0-20251007.062658-1.aar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium
index 2903ef6..962df855 100644
--- a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Saved State Compose
 Short Name: savedstate-compose-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/savedstate/savedstate-compose-android/1.4.0-SNAPSHOT/savedstate-compose-android-1.4.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/savedstate/savedstate-compose-android/1.4.0-SNAPSHOT/savedstate-compose-android-1.4.0-20251007.062658-1.aar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium
index 458e159..1bf82ea 100644
--- a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: SavedState Kotlin Extensions
 Short Name: savedstate-ktx
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/savedstate/savedstate-ktx/1.4.0-SNAPSHOT/savedstate-ktx-1.4.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/savedstate/savedstate-ktx/1.4.0-SNAPSHOT/savedstate-ktx-1.4.0-20251007.062658-1.aar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium b/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium
index efa0f1b7..89868e5b 100644
--- a/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium
@@ -1,6 +1,6 @@
 Name: Sliding Pane Layout
 Short Name: slidingpanelayout
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/slidingpanelayout/slidingpanelayout/1.3.0-SNAPSHOT/slidingpanelayout-1.3.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/slidingpanelayout/slidingpanelayout/1.3.0-SNAPSHOT/slidingpanelayout-1.3.0-20251007.062658-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_swiperefreshlayout_swiperefreshlayout/README.chromium b/third_party/androidx/committed/libs/androidx_swiperefreshlayout_swiperefreshlayout/README.chromium
index 4c7d3fac..5bc468e3 100644
--- a/third_party/androidx/committed/libs/androidx_swiperefreshlayout_swiperefreshlayout/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_swiperefreshlayout_swiperefreshlayout/README.chromium
@@ -1,6 +1,6 @@
 Name: Swipe Refresh Layout
 Short Name: swiperefreshlayout
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/swiperefreshlayout/swiperefreshlayout/1.2.0-SNAPSHOT/swiperefreshlayout-1.2.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/swiperefreshlayout/swiperefreshlayout/1.2.0-SNAPSHOT/swiperefreshlayout-1.2.0-20251007.062658-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium b/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium
index f389322..21800306 100644
--- a/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium
@@ -1,6 +1,6 @@
 Name: UIAutomator
 Short Name: uiautomator
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/test/uiautomator/uiautomator/2.4.0-SNAPSHOT/uiautomator-2.4.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/test/uiautomator/uiautomator/2.4.0-SNAPSHOT/uiautomator-2.4.0-20251007.062658-1.aar
 Version: 2.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_transition_transition/README.chromium b/third_party/androidx/committed/libs/androidx_transition_transition/README.chromium
index aa471b8..8c65d57 100644
--- a/third_party/androidx/committed/libs/androidx_transition_transition/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_transition_transition/README.chromium
@@ -1,6 +1,6 @@
 Name: Transition
 Short Name: transition
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/transition/transition/1.7.0-SNAPSHOT/transition-1.7.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/transition/transition/1.7.0-SNAPSHOT/transition-1.7.0-20251007.062658-1.aar
 Version: 1.7.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium b/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium
index a571e37e..18c0384 100644
--- a/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium
@@ -1,6 +1,6 @@
 Name: ViewPager2
 Short Name: viewpager2
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/viewpager2/viewpager2/1.2.0-SNAPSHOT/viewpager2-1.2.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/viewpager2/viewpager2/1.2.0-SNAPSHOT/viewpager2-1.2.0-20251007.062658-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium b/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium
index 1aea9fa..bbee20b 100644
--- a/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium
@@ -1,6 +1,6 @@
 Name: Webkit
 Short Name: webkit
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/webkit/webkit/1.15.0-SNAPSHOT/webkit-1.15.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/webkit/webkit/1.15.0-SNAPSHOT/webkit-1.15.0-20251007.062658-1.aar
 Version: 1.15.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium b/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium
index 2ed8c50..055657aa 100644
--- a/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium
@@ -1,6 +1,6 @@
 Name: WindowManager Sidecar
 Short Name: sidecar
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/window/sidecar/sidecar/1.0.0-SNAPSHOT/sidecar-1.0.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/window/sidecar/sidecar/1.0.0-SNAPSHOT/sidecar-1.0.0-20251007.062658-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_window_window/README.chromium b/third_party/androidx/committed/libs/androidx_window_window/README.chromium
index 1dff58f1..8e374c4 100644
--- a/third_party/androidx/committed/libs/androidx_window_window/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_window_window/README.chromium
@@ -1,6 +1,6 @@
 Name: WindowManager
 Short Name: window
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/window/window/1.6.0-SNAPSHOT/window-1.6.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/window/window/1.6.0-SNAPSHOT/window-1.6.0-20251007.062658-1.aar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_window_window_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_window_window_core_android/README.chromium
index bf587f6..3bf40d8 100644
--- a/third_party/androidx/committed/libs/androidx_window_window_core_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_window_window_core_android/README.chromium
@@ -1,6 +1,6 @@
 Name: WindowManager Core
 Short Name: window-core-android
-URL: https://androidx.dev/snapshots/builds/14214693/artifacts/repository/androidx/window/window-core-android/1.6.0-SNAPSHOT/window-core-android-1.6.0-20251004.032114-1.aar
+URL: https://androidx.dev/snapshots/builds/14224118/artifacts/repository/androidx/window/window-core-android/1.6.0-SNAPSHOT/window-core-android-1.6.0-20251007.062658-1.aar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/angle b/third_party/angle
index 5c58505..bd1efa9 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 5c58505be7f434c053f4066081001373d21c4bd1
+Subproject commit bd1efa99f9e19b882d8f09000a315ced599e86c3
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 01e8a8e9..9fbd5a4 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -2064,6 +2064,9 @@
 // cross-origin requests.
 BASE_FEATURE(kReducedReferrerGranularity, base::FEATURE_ENABLED_BY_DEFAULT);
 
+BASE_FEATURE(kRefactorCompositorThreadEventQueue,
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 BASE_FEATURE_PARAM(std::string,
                    kUserAgentFrozenBuildVersion,
                    &kReduceUserAgentMinorVersion,
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 01473828..b550c61 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -1562,6 +1562,11 @@
 // Parameters for kReduceUserAgentPlatformOsCpu;
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kReducedReferrerGranularity);
 
+// Refactor CompositorThreadEventQueue to separate event queuing and coalescing.
+// When disabled, CompositorThreadEventQueue coalesces input events in
+// CompositorThreadEventQueue::Queue itself.
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kRefactorCompositorThreadEventQueue);
+
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
     kReleaseResourceDecodedDataOnMemoryPressure);
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
diff --git a/third_party/blink/public/devtools_protocol/domains/Browser.pdl b/third_party/blink/public/devtools_protocol/domains/Browser.pdl
index b973cd9..644661f 100644
--- a/third_party/blink/public/devtools_protocol/domains/Browser.pdl
+++ b/third_party/blink/public/devtools_protocol/domains/Browser.pdl
@@ -103,19 +103,19 @@
       closeTabSearch
       openGlic
 
-  # Set permission settings for given requesting and embedding origins.
+  # Set permission settings for given embedding and embedded origins.
   experimental command setPermission
     parameters
       # Descriptor of permission to override.
       PermissionDescriptor permission
       # Setting of the permission.
       PermissionSetting setting
-      # Requesting origin the permission applies to, all origins if not specified.
+      # Embedding origin the permission applies to, all origins if not specified.
       optional string origin
-      # Embedding origin the permission applies to. It is ignored unless the requesting origin is
-      # present and valid. If the requesting origin is provided but the embedding origin isn't, the
-      # requesting origin is used as the embedding origin.
-      optional string embeddingOrigin
+      # Embedded origin the permission applies to. It is ignored unless the embedding origin is
+      # present and valid. If the embedding origin is provided but the embedded origin isn't, the
+      # embedding origin is used as the embedded origin.
+      optional string embeddedOrigin
       # Context to override. When omitted, default browser context is used.
       optional BrowserContextID browserContextId
 
diff --git a/third_party/blink/public/devtools_protocol/domains/Network.pdl b/third_party/blink/public/devtools_protocol/domains/Network.pdl
index 03538fe..c2cdeb1b 100644
--- a/third_party/blink/public/devtools_protocol/domains/Network.pdl
+++ b/third_party/blink/public/devtools_protocol/domains/Network.pdl
@@ -1090,8 +1090,8 @@
   experimental type NetworkConditions extends object
     properties
       # Only matching requests will be affected by these conditions. Patterns use the URLPattern constructor string
-      # syntax (https://urlpattern.spec.whatwg.org/). If the pattern is empty, all requests are matched (including p2p
-      # connections).
+      # syntax (https://urlpattern.spec.whatwg.org/) and must be absolute. If the pattern is empty, all requests are
+      # matched (including p2p connections).
       string urlPattern
       # Minimum latency from request sent to response headers received (ms).
       number latency
@@ -1269,7 +1269,7 @@
   experimental command setBlockedURLs
     parameters
       # URL patterns to block. Patterns use the URLPattern constructor string syntax
-      # (https://urlpattern.spec.whatwg.org/). Example: `*://*:*/*.css`.
+      # (https://urlpattern.spec.whatwg.org/) and must be absolute. Example: `*://*:*/*.css`.
       optional array of string urlPatterns
       # URL patterns to block. Wildcards ('*') are allowed.
       deprecated optional array of string urls
diff --git a/third_party/blink/public/devtools_protocol/domains/Preload.pdl b/third_party/blink/public/devtools_protocol/domains/Preload.pdl
index fd3222a..8e9c8a2 100644
--- a/third_party/blink/public/devtools_protocol/domains/Preload.pdl
+++ b/third_party/blink/public/devtools_protocol/domains/Preload.pdl
@@ -57,6 +57,7 @@
     enum
       Prefetch
       Prerender
+      PrerenderUntilScript
 
   # Corresponds to mojom::SpeculationTargetHint.
   # See https://github.com/WICG/nav-speculation/blob/main/triggers.md#window-name-targeting-hints
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index bb3d7a11..69c9a8b 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -1397,8 +1397,8 @@
   kAnchorClickDispatchForNonConnectedNode = 1971,
   kHTMLParseErrorNestedForm = 1972,
   kFontShapingNotDefGlyphObserved = 1973,
-  kPostMessageOutgoingWouldBeBlockedByConnectSrc = 1974,
-  kPostMessageIncomingWouldBeBlockedByConnectSrc = 1975,
+  kOBSOLETE_PostMessageOutgoingWouldBeBlockedByConnectSrc = 1974,
+  kOBSOLETE_PostMessageIncomingWouldBeBlockedByConnectSrc = 1975,
   kCrossOriginPropertyAccess = 1977,
   kCrossOriginPropertyAccessFromOpener = 1978,
   kCredentialManagerCreate = 1979,
diff --git a/third_party/blink/public/mojom/worker/shared_worker_factory.mojom b/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
index 20d26a7..92920f8 100644
--- a/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
+++ b/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
@@ -11,6 +11,7 @@
 import "third_party/blink/public/mojom/renderer_preference_watcher.mojom";
 import "third_party/blink/public/mojom/frame/policy_container.mojom";
 import "third_party/blink/public/mojom/frame/reporting_observer.mojom";
+import "third_party/blink/public/mojom/fingerprinting_protection/noise_token.mojom";
 import "third_party/blink/public/mojom/renderer_preferences.mojom";
 import "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom";
 import "third_party/blink/public/mojom/service_worker/service_worker_container.mojom";
@@ -116,5 +117,12 @@
 
       // Used by a DocumentIsolationPolicyReporter to send reports to the
       // ReportingObservers.
-      pending_receiver<ReportingObserver>? dip_reporting_observer_receiver);
+      pending_receiver<ReportingObserver>? dip_reporting_observer_receiver,
+
+      // The 64 bit hash value used for canvas noising for this SharedWorker. A
+      // null indicates canvas noising should be disabled for this SharedWorker.
+      // This is the initial value used to indicate the enablement state of the
+      // canvas noising feature and the initial seed value the noising should use,
+      // and must be available to the renderer prior to JavaScript execution.
+      NoiseToken? canvas_noise_token);
 };
diff --git a/third_party/blink/public/web/web_dom_message_event.h b/third_party/blink/public/web/web_dom_message_event.h
index 1bfbffd..4cf37b9e 100644
--- a/third_party/blink/public/web/web_dom_message_event.h
+++ b/third_party/blink/public/web/web_dom_message_event.h
@@ -32,12 +32,7 @@
 
 #include <optional>
 
-#include "base/unguessable_token.h"
-#include "third_party/blink/public/common/messaging/message_port_channel.h"
-#include "third_party/blink/public/common/messaging/transferable_message.h"
 #include "third_party/blink/public/platform/web_common.h"
-#include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/public/web/web_document.h"
 #include "third_party/blink/public/web/web_dom_event.h"
 #include "third_party/blink/public/web/web_serialized_script_value.h"
 
@@ -47,23 +42,14 @@
 
 namespace blink {
 
-class WebFrame;
-
 // An interface for posting message events to the target frame. The message
 // events are used for communication between documents and described here:
 // http://www.w3.org/TR/2012/WD-webmessaging-20120313/#terminology
 class BLINK_EXPORT WebDOMMessageEvent : public WebDOMEvent {
  public:
-  WebDOMMessageEvent(const WebSerializedScriptValue& message_data,
-                     const WebString& origin = WebString(),
-                     const WebFrame* source_frame = nullptr,
-                     const WebDocument& target_document = WebDocument(),
-                     std::vector<MessagePortChannel> ports =
-                         std::vector<MessagePortChannel>());
+  explicit WebDOMMessageEvent(const WebSerializedScriptValue& message_data);
   WebDOMMessageEvent() = default;
 
-  WebString Origin();
-
 #if INSIDE_BLINK
   explicit WebDOMMessageEvent(MessageEvent* e) : WebDOMEvent(e) {}
 #endif
diff --git a/third_party/blink/public/web/web_node.h b/third_party/blink/public/web/web_node.h
index b18801f..05d164ac7 100644
--- a/third_party/blink/public/web/web_node.h
+++ b/third_party/blink/public/web/web_node.h
@@ -84,7 +84,7 @@
   bool IsConnected() const;
 
   bool Contains(const WebNode*) const;
-  bool ContainsIncludingHostElements(const WebNode*) const;
+  bool ContainsViaFlatTree(const WebNode*) const;
 
   WebNode ParentNode() const;
   WebNode ParentOrShadowHostNode() const;
diff --git a/third_party/blink/public/web/web_shared_worker.h b/third_party/blink/public/web/web_shared_worker.h
index 19622b4..d8be220 100644
--- a/third_party/blink/public/web/web_shared_worker.h
+++ b/third_party/blink/public/web/web_shared_worker.h
@@ -38,6 +38,7 @@
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/network/public/mojom/content_security_policy.mojom-shared.h"
 #include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "third_party/blink/public/common/fingerprinting_protection/noise_token.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "third_party/blink/public/mojom/browser_interface_broker.mojom-shared.h"
@@ -101,7 +102,8 @@
       CrossVariantMojoReceiver<mojom::ReportingObserverInterfaceBase>
           coep_reporting_observer,
       CrossVariantMojoReceiver<mojom::ReportingObserverInterfaceBase>
-          dip_reporting_observer);
+          dip_reporting_observer,
+      std::optional<NoiseToken> canvas_noise_token);
 
   // Sends a connect event to the SharedWorker context.
   virtual void Connect(int connection_request_id,
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 380d721..9ddb484 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -284,6 +284,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_rendering_context_2d_settings.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_2d_gpu_transfer_option.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_2d_gpu_transfer_option.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_tone_mapping.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_tone_mapping.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_capture_handle.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_capture_handle.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_capture_handle_config.cc",
@@ -482,8 +484,6 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_buffer_descriptor.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_configuration.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_configuration.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_tone_mapping.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_tone_mapping.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_color_dict.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_color_dict.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_color_state_descriptor.cc",
@@ -1486,6 +1486,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_power_preference.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_text_rendering.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_text_rendering.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_tone_mapping_mode.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_tone_mapping_mode.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_will_read_frequently.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_will_read_frequently.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_capture_start_focus_behavior.cc",
@@ -1568,8 +1570,6 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_buffer_map_state.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_alpha_mode.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_alpha_mode.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_tone_mapping_mode.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_tone_mapping_mode.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_compare_function.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_compare_function.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gpu_compilation_message_type.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_core.gni b/third_party/blink/renderer/bindings/idl_in_core.gni
index 3f7db5cce..3db9a27 100644
--- a/third_party/blink/renderer/bindings/idl_in_core.gni
+++ b/third_party/blink/renderer/bindings/idl_in_core.gni
@@ -288,8 +288,7 @@
   "//third_party/blink/renderer/core/fetch/response.idl",
   "//third_party/blink/renderer/core/fetch/response_init.idl",
   "//third_party/blink/renderer/core/fetch/retry_options.idl",
-  "//third_party/blink/renderer/core/fetch/window_fetch.idl",
-  "//third_party/blink/renderer/core/fetch/worker_fetch.idl",
+  "//third_party/blink/renderer/core/fetch/window_or_worker_global_scope_fetch.idl",
   "//third_party/blink/renderer/core/fileapi/blob.idl",
   "//third_party/blink/renderer/core/fileapi/blob_property_bag.idl",
   "//third_party/blink/renderer/core/fileapi/file.idl",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index 57c9d06..d51e166 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -101,8 +101,7 @@
   "//third_party/blink/renderer/modules/cache_storage/cache_query_options.idl",
   "//third_party/blink/renderer/modules/cache_storage/cache_storage.idl",
   "//third_party/blink/renderer/modules/cache_storage/multi_cache_query_options.idl",
-  "//third_party/blink/renderer/modules/cache_storage/window_cache_storage.idl",
-  "//third_party/blink/renderer/modules/cache_storage/worker_cache_storage.idl",
+  "//third_party/blink/renderer/modules/cache_storage/window_or_worker_global_scope_cache_storage.idl",
   "//third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.idl",
   "//third_party/blink/renderer/modules/canvas/canvas2d/canvas_2d_recorder_context.idl",
   "//third_party/blink/renderer/modules/canvas/canvas2d/canvas_filter.idl",
@@ -121,8 +120,7 @@
   "//third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_module.idl",
   "//third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.idl",
   "//third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context.idl",
-  "//third_party/blink/renderer/modules/canvas/imagebitmap/window_create_image_bitmap.idl",
-  "//third_party/blink/renderer/modules/canvas/imagebitmap/worker_create_image_bitmap.idl",
+  "//third_party/blink/renderer/modules/canvas/imagebitmap/window_or_worker_global_scope_create_image_bitmap.idl",
   "//third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_module.idl",
   "//third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl",
   "//third_party/blink/renderer/modules/clipboard/clipboard.idl",
diff --git a/third_party/blink/renderer/core/animation/animation_test.cc b/third_party/blink/renderer/core/animation/animation_test.cc
index ff2ea58..d7da454 100644
--- a/third_party/blink/renderer/core/animation/animation_test.cc
+++ b/third_party/blink/renderer/core/animation/animation_test.cc
@@ -2770,7 +2770,6 @@
           ));
 
         let trigger = new TimelineTrigger({
-          type: "alternate",
           timeline: new ViewTimeline({
             subject: document.getElementById('subject'), axis: "y"
           }),
diff --git a/third_party/blink/renderer/core/animation/css/css_animation_data.cc b/third_party/blink/renderer/core/animation/css/css_animation_data.cc
index 9117038..a0ec96ee 100644
--- a/third_party/blink/renderer/core/animation/css/css_animation_data.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animation_data.cc
@@ -19,7 +19,6 @@
   range_end_list_.push_back(InitialRangeEnd());
   composition_list_.push_back(InitialComposition());
   timeline_trigger_name_list_.push_back(InitialTimelineTriggerName());
-  timeline_trigger_behavior_list_.push_back(InitialTimelineTriggerBehavior());
   timeline_trigger_source_list_.push_back(InitialTimelineTriggerSource());
   timeline_trigger_range_start_list_.push_back(
       InitialTimelineTriggerRangeStart());
diff --git a/third_party/blink/renderer/core/animation/css/css_animation_data.h b/third_party/blink/renderer/core/animation/css/css_animation_data.h
index baa161d..df73d0d 100644
--- a/third_party/blink/renderer/core/animation/css/css_animation_data.h
+++ b/third_party/blink/renderer/core/animation/css/css_animation_data.h
@@ -69,9 +69,6 @@
       const {
     return timeline_trigger_name_list_;
   }
-  const Vector<EAnimationTriggerBehavior>& TimelineTriggerBehaviorList() const {
-    return timeline_trigger_behavior_list_;
-  }
   const Vector<std::optional<TimelineOffset>>& TimelineTriggerRangeStartList()
       const {
     return timeline_trigger_range_start_list_;
@@ -124,9 +121,6 @@
   HeapVector<Member<const ScopedCSSName>>& TimelineTriggerNameList() {
     return timeline_trigger_name_list_;
   }
-  Vector<EAnimationTriggerBehavior>& TimelineTriggerBehaviorList() {
-    return timeline_trigger_behavior_list_;
-  }
   Vector<std::optional<TimelineOffset>>& TimelineTriggerRangeStartList() {
     return timeline_trigger_range_start_list_;
   }
@@ -178,9 +172,6 @@
     return EffectModel::CompositeOperation::kCompositeReplace;
   }
   static const ScopedCSSName* InitialTimelineTriggerName() { return nullptr; }
-  static EAnimationTriggerBehavior InitialTimelineTriggerBehavior() {
-    return EAnimationTriggerBehavior::kOnce;
-  }
   static std::optional<TimelineOffset> InitialTimelineTriggerRangeStart() {
     return std::nullopt;
   }
@@ -210,7 +201,6 @@
   Vector<EffectModel::CompositeOperation> composition_list_;
 
   HeapVector<Member<const ScopedCSSName>> timeline_trigger_name_list_;
-  Vector<EAnimationTriggerBehavior> timeline_trigger_behavior_list_;
   Vector<std::optional<TimelineOffset>> timeline_trigger_range_start_list_;
   Vector<std::optional<TimelineOffset>> timeline_trigger_range_end_list_;
   Vector<TimelineOffsetOrAuto> timeline_trigger_exit_range_start_list_;
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc
index aa646e6f..12d62094 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -3564,7 +3564,6 @@
     case CSSPropertyID::kTextOrientation:
     case CSSPropertyID::kTimelineScope:
     case CSSPropertyID::kTimelineTriggerName:
-    case CSSPropertyID::kTimelineTriggerBehavior:
     case CSSPropertyID::kTimelineTriggerRangeStart:
     case CSSPropertyID::kTimelineTriggerRangeEnd:
     case CSSPropertyID::kTimelineTriggerExitRangeStart:
diff --git a/third_party/blink/renderer/core/animation/css/css_animations_test.cc b/third_party/blink/renderer/core/animation/css/css_animations_test.cc
index b0a850ba..0d7e809 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations_test.cc
@@ -1441,7 +1441,7 @@
         height: 50px;
         width: 50px;
         animation: myAnim linear 0.5s forwards;
-        timeline-trigger: --trigger once;
+        timeline-trigger: --trigger;
         animation-trigger: --trigger;
       }
      .scroller {
@@ -1537,7 +1537,7 @@
         height: 50px;
         width: 50px;
         animation: myAnim linear 0.5s forwards;
-        timeline-trigger: --trigger scroll() once 25% 75%;
+        timeline-trigger: --trigger scroll() 25% 75%;
         animation-trigger: --trigger;
       }
      .scroller {
@@ -1588,7 +1588,7 @@
         height: 50px;
         width: 50px;
         animation: myAnim linear 0.5s forwards;
-        timeline-trigger: --trigger view() alternate contain 10% contain 90%;
+        timeline-trigger: --trigger view() contain 10% contain 90%;
            animation-trigger: --trigger;
       }
      .scroller {
@@ -1639,7 +1639,7 @@
         height: 50px;
         width: 50px;
         animation: myAnim linear 0.5s forwards;
-        timeline-trigger: --trigger view() repeat contain 10% contain 90%
+        timeline-trigger: --trigger view() contain 10% contain 90%
         cover 1% cover 99%;
         animation-trigger: --trigger;
       }
@@ -1694,7 +1694,7 @@
       }
       #target {
         animation: myAnim linear 0.5s forwards;
-        timeline-trigger: --trigger --viewtimeline repeat contain 10% contain 90%;
+        timeline-trigger: --trigger --viewtimeline contain 10% contain 90%;
         animation-trigger: --trigger;
       }
      .scroller {
@@ -1746,10 +1746,10 @@
         animation: stretch linear 0.5s forwards;
       }
       .view_trigger {
-        timeline-trigger: --trigger --viewtimeline repeat contain 10% contain 90%;
+        timeline-trigger: --trigger --viewtimeline contain 10% contain 90%;
       }
       .scroll_trigger {
-        timeline-trigger: --trigger --scrolltimeline repeat contain 10% contain 90%;
+        timeline-trigger: --trigger --scrolltimeline contain 10% contain 90%;
       }
      .scroller {
         overflow-y: scroll;
@@ -1833,22 +1833,22 @@
         animation: stretch linear 0.5s forwards;
       }
       .normal_trigger {
-        timeline-trigger: --normal-trigger view() repeat;
+        timeline-trigger: --normal-trigger view();
       }
       .normal_trigger2 {
-        timeline-trigger: --normal-trigger view() repeat;
+        timeline-trigger: --normal-trigger view();
       }
       .contain10_trigger {
-        timeline-trigger: --contain10-trigger view() once contain 10%;
+        timeline-trigger: --contain10-trigger view() contain 10%;
       }
       .contain10_trigger2 {
-        timeline-trigger: --contain10-trigger view() once contain 10%;
+        timeline-trigger: --contain10-trigger view() contain 10%;
       }
       .contain90_trigger {
-        timeline-trigger: --contain90-trigger view() once contain 90%;
+        timeline-trigger: --contain90-trigger view() contain 90%;
       }
       .cover90_trigger {
-        timeline-trigger: --cover90-trigger view() once cover 90%;
+        timeline-trigger: --cover90-trigger view() cover 90%;
       }
 
      .scroller {
@@ -1915,7 +1915,7 @@
         height: 10px;
         width: 10px;
         animation: stretch linear 0.5s forwards;
-        timeline-trigger: --trigger view() once contain 10% contain 90%;
+        timeline-trigger: --trigger view() contain 10% contain 90%;
         animation-trigger: --trigger;
       }
       .scroll_tl {
@@ -2012,7 +2012,7 @@
         height: 10px;
         width: 10px;
         animation: stretch linear 0.5s forwards;
-        timeline-trigger: --trigger view() once 100px 300px;
+        timeline-trigger: --trigger view() 100px 300px;
         animation-trigger: --trigger;
 
       }
@@ -2058,11 +2058,11 @@
       }
       .trigger1 {
         animation-trigger: trigger(--trigger1, enter play);
-        timeline-trigger: --trigger1 view() once contain 10% contain 90%;
+        timeline-trigger: --trigger1 view() contain 10% contain 90%;
       }
       .trigger2 {
         animation-trigger: trigger(--trigger2, exit pause);
-        timeline-trigger: --trigger2 view() once contain 10% contain 90%;
+        timeline-trigger: --trigger2 view() contain 10% contain 90%;
       }
      .scroller {
         overflow-y: scroll;
diff --git a/third_party/blink/renderer/core/css/build.gni b/third_party/blink/renderer/core/css/build.gni
index c35f136..1d95fe0 100644
--- a/third_party/blink/renderer/core/css/build.gni
+++ b/third_party/blink/renderer/core/css/build.gni
@@ -945,6 +945,7 @@
   "resolver/style_cascade_test.cc",
   "resolver/style_resolver_test.cc",
   "rule_feature_set_test.cc",
+  "rule_set_diff_test.cc",
   "rule_set_test.cc",
   "selector_checker_test.cc",
   "selector_filter_test.cc",
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index c072f93..90ba233 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -1048,19 +1048,6 @@
       runtime_flag: "AnimationTrigger",
     },
     {
-      name: "timeline-trigger-behavior",
-      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "InitialValue"],
-      style_builder_template: "animation",
-      style_builder_template_args: {
-        attribute: "TimelineTriggerBehavior",
-      },
-      keywords: ["once", "repeat", "alternate", "state"],
-      typedom_types: ["Keyword"],
-      separator: ",",
-      valid_for_marker: true,
-      runtime_flag: "AnimationTrigger",
-    },
-    {
       name: "timeline-trigger-name",
       property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "InitialValue"],
       field_group: "*",
@@ -4936,7 +4923,6 @@
       default_value: "visible",
       typedom_types: ["Keyword"],
       computable: false,
-      valid_for_permission_element: true,
       invalidate: ["layout"],
     },
     {
@@ -9016,9 +9002,8 @@
       name: "timeline-trigger",
       longhands: [
         "timeline-trigger-name", "timeline-trigger-source",
-        "timeline-trigger-behavior", "timeline-trigger-range-start",
-        "timeline-trigger-range-end", "timeline-trigger-exit-range-start",
-        "timeline-trigger-exit-range-end",
+        "timeline-trigger-range-start", "timeline-trigger-range-end",
+        "timeline-trigger-exit-range-start", "timeline-trigger-exit-range-end",
       ],
       property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"],
       supports_incremental_style: false,
diff --git a/third_party/blink/renderer/core/css/css_property_equality.cc b/third_party/blink/renderer/core/css/css_property_equality.cc
index 362c4d55..53fae0d 100644
--- a/third_party/blink/renderer/core/css/css_property_equality.cc
+++ b/third_party/blink/renderer/core/css/css_property_equality.cc
@@ -987,7 +987,6 @@
     case CSSPropertyID::kAnimationTrigger:
     case CSSPropertyID::kScrollTimelineAxis:
     case CSSPropertyID::kScrollTimelineName:
-    case CSSPropertyID::kTimelineTriggerBehavior:
     case CSSPropertyID::kTimelineTriggerName:
     case CSSPropertyID::kTimelineTriggerRangeStart:
     case CSSPropertyID::kTimelineTriggerRangeEnd:
diff --git a/third_party/blink/renderer/core/css/css_selector.cc b/third_party/blink/renderer/core/css/css_selector.cc
index 8cad051..e3f45ec 100644
--- a/third_party/blink/renderer/core/css/css_selector.cc
+++ b/third_party/blink/renderer/core/css/css_selector.cc
@@ -834,7 +834,7 @@
 void CSSSelector::Show(int indent) const {
   printf("%*sSelectorText(): %s\n", indent, "", SelectorText().Ascii().c_str());
   printf("%*smatch_: %d\n", indent, "", Match());
-  if (Match() != kTag && Match() != kUniversalTag) {
+  if (Match() != kTag && Match() != kUniversalTag && !IsPseudoParent()) {
     printf("%*sValue(): %s\n", indent, "", Value().Ascii().c_str());
   }
   printf("%*sGetPseudoType(): %d\n", indent, "", GetPseudoType());
diff --git a/third_party/blink/renderer/core/css/css_selector.h b/third_party/blink/renderer/core/css/css_selector.h
index 9cf80c2..3654396a 100644
--- a/third_party/blink/renderer/core/css/css_selector.h
+++ b/third_party/blink/renderer/core/css/css_selector.h
@@ -657,6 +657,10 @@
   // position like :first-of-type and :nth-child().
   bool IsChildIndexedSelector() const;
 
+  bool IsPseudoParent() const {
+    return Match() == kPseudoClass && GetPseudoType() == kPseudoParent;
+  }
+
   void Trace(Visitor* visitor) const;
 
   static String FormatPseudoTypeForDebugging(PseudoType);
@@ -879,7 +883,7 @@
                                   bool match_lower_case = false) {
   DCHECK_NE(Match(), static_cast<unsigned>(kTag));
   DCHECK_NE(Match(), static_cast<unsigned>(kUniversalTag));
-  DCHECK(!(Match() == kPseudoClass && GetPseudoType() == kPseudoParent));
+  DCHECK(!IsPseudoParent());
   if (match_lower_case && !HasRareData() && !IsASCIILower(value)) {
     CreateRareData();
   }
@@ -1014,6 +1018,7 @@
 inline const AtomicString& CSSSelector::Value() const {
   DCHECK_NE(Match(), static_cast<unsigned>(kTag));
   DCHECK_NE(Match(), static_cast<unsigned>(kUniversalTag));
+  DCHECK(!IsPseudoParent());
   if (HasRareData()) {
     return data_.rare_data_->matching_value_;
   }
diff --git a/third_party/blink/renderer/core/css/css_selector_test.cc b/third_party/blink/renderer/core/css/css_selector_test.cc
index c8c05bd7..da204fc 100644
--- a/third_party/blink/renderer/core/css/css_selector_test.cc
+++ b/third_party/blink/renderer/core/css/css_selector_test.cc
@@ -636,4 +636,16 @@
   EXPECT_EQ(list, list->Renest(b));
 }
 
+#if DCHECK_IS_ON()
+
+TEST(CSSSelector, ShowWithParentPseudo) {
+  test::TaskEnvironment task_environment;
+  CSSSelectorList* list = ParseSelectorList("& .x");
+  ASSERT_TRUE(list);
+  ASSERT_TRUE(list->First());
+  list->First()->Show();  // Don't crash.
+}
+
+#endif  // DCHECK_IS_ON()
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator.cc b/third_party/blink/renderer/core/css/media_query_evaluator.cc
index 62cda79..941ad49 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator.cc
@@ -1761,6 +1761,13 @@
     return KleeneValue::kUnknown;
   }
 
+  if (RuntimeEnabledFeatures::CSSCustomMediaEnabled() &&
+      feature.IsCustomMedia() &&
+      CSSVariableParser::IsValidVariableName(feature.Name())) {
+    // TODO(crbug.com/40781325): Support evaluation of custom-media queries.
+    return KleeneValue::kUnknown;
+  }
+
   if (feature.HasStyleRange() ||
       CSSVariableParser::IsValidVariableName(feature.Name())) {
     return EvalStyleFeature(feature, result_flags);
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
index 3003a02..7d5b73f7b 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
@@ -1824,4 +1824,17 @@
   EXPECT_EQ(entry.metrics.begin()->value, IdentifiableToken(Scripting::kNone));
 }
 
+TEST(MediaQueryEvaluatorTest, TestQueriesWithUndefinedCustomMedias) {
+  MediaValuesCached::MediaValuesCachedData data;
+  auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
+  MediaQueryEvaluator* media_query_evaluator =
+      MakeGarbageCollected<MediaQueryEvaluator>(media_values);
+
+  MediaQueryEvaluatorTestCase test_cases[] = {
+      {"(--undefined)", false},
+  };
+
+  TestMQEvaluator(test_cases, media_query_evaluator);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/media_query_exp.cc b/third_party/blink/renderer/core/css/media_query_exp.cc
index fa53215..ec65801 100644
--- a/third_party/blink/renderer/core/css/media_query_exp.cc
+++ b/third_party/blink/renderer/core/css/media_query_exp.cc
@@ -462,13 +462,13 @@
 MediaQueryExp::MediaQueryExp(const String& media_feature,
                              const MediaQueryExpValue& value)
     : MediaQueryExp(media_feature,
-                    MediaQueryExpBounds(MediaQueryExpComparison(value))) {}
+                    MediaQueryExpBounds(MediaQueryExpComparison(value)),
+                    Type::kMediaFeature) {}
 
 MediaQueryExp::MediaQueryExp(const String& media_feature,
-                             const MediaQueryExpBounds& bounds)
-    : type_(Type::kMediaFeature),
-      media_feature_(media_feature),
-      bounds_(bounds) {}
+                             const MediaQueryExpBounds& bounds,
+                             Type type)
+    : type_(type), media_feature_(media_feature), bounds_(bounds) {}
 
 MediaQueryExp::MediaQueryExp(const CSSUnparsedDeclarationValue& reference_value,
                              const MediaQueryExpBounds& bounds)
@@ -615,7 +615,11 @@
 
 MediaQueryExp MediaQueryExp::Create(const AtomicString& media_feature,
                                     const MediaQueryExpBounds& bounds) {
-  return MediaQueryExp(media_feature, bounds);
+  return MediaQueryExp(media_feature, bounds, Type::kMediaFeature);
+}
+
+MediaQueryExp MediaQueryExp::Create(const AtomicString& custom_media) {
+  return MediaQueryExp(custom_media, MediaQueryExpBounds(), Type::kCustomMedia);
 }
 
 MediaQueryExp MediaQueryExp::Create(const MediaQueryExpValue& reference_value,
@@ -645,16 +649,18 @@
   // <mf-boolean> e.g. (color)
   // <mf-plain>  e.g. (width: 100px)
   if (!bounds_.IsRange()) {
-    if (HasMediaFeature()) {
+    if (HasMediaFeature() || IsCustomMedia()) {
       result.Append(media_feature_);
     } else {
       result.Append(reference_value_->CssText());
     }
     if (bounds_.right.IsValid()) {
+      DCHECK(!IsCustomMedia());
       result.Append(": ");
       result.Append(bounds_.right.value.CssText());
     }
   } else {
+    DCHECK(!IsCustomMedia());
     if (bounds_.left.IsValid()) {
       result.Append(bounds_.left.value.CssText());
       result.Append(" ");
diff --git a/third_party/blink/renderer/core/css/media_query_exp.h b/third_party/blink/renderer/core/css/media_query_exp.h
index 629aa449..c2b4d34 100644
--- a/third_party/blink/renderer/core/css/media_query_exp.h
+++ b/third_party/blink/renderer/core/css/media_query_exp.h
@@ -282,6 +282,7 @@
                               bool supports_element_dependent);
   static MediaQueryExp Create(const AtomicString& media_feature,
                               const MediaQueryExpBounds&);
+  static MediaQueryExp Create(const AtomicString& custom_media);
   static MediaQueryExp Create(const MediaQueryExpValue& reference_value,
                               const MediaQueryExpBounds&);
   static MediaQueryExp Invalid() { return MediaQueryExp(); }
@@ -293,9 +294,10 @@
   bool IsValid() const { return type_ != Type::kInvalid; }
   bool HasMediaFeature() const { return type_ == Type::kMediaFeature; }
   bool HasStyleRange() const { return type_ == Type::kStyleRange; }
+  bool IsCustomMedia() const { return type_ == Type::kCustomMedia; }
 
   const AtomicString& MediaFeature() const {
-    DCHECK(HasMediaFeature());
+    DCHECK(HasMediaFeature() || IsCustomMedia());
     return media_feature_;
   }
 
@@ -323,11 +325,13 @@
   unsigned GetUnitFlags() const;
 
  private:
-  enum class Type { kMediaFeature, kStyleRange, kInvalid };
+  enum class Type { kMediaFeature, kCustomMedia, kStyleRange, kInvalid };
 
   MediaQueryExp() = default;
   MediaQueryExp(const String& media_feature, const MediaQueryExpValue&);
-  MediaQueryExp(const String& media_feature, const MediaQueryExpBounds&);
+  MediaQueryExp(const String& media_feature,
+                const MediaQueryExpBounds&,
+                Type type);
   MediaQueryExp(const CSSUnparsedDeclarationValue& reference_value,
                 const MediaQueryExpBounds&);
 
@@ -393,7 +397,7 @@
   void Trace(Visitor*) const override;
 
   const String& Name() const {
-    DCHECK(HasMediaFeature());
+    DCHECK(HasMediaFeature() || IsCustomMedia());
     return exp_.MediaFeature();
   }
 
@@ -404,6 +408,7 @@
 
   bool HasMediaFeature() const { return exp_.HasMediaFeature(); }
   bool HasStyleRange() const { return exp_.HasStyleRange(); }
+  bool IsCustomMedia() const { return exp_.IsCustomMedia(); }
 
   const MediaQueryExpBounds& Bounds() const { return exp_.Bounds(); }
 
diff --git a/third_party/blink/renderer/core/css/parser/media_query_parser.cc b/third_party/blink/renderer/core/css/parser/media_query_parser.cc
index 955c8ba..fef0d428 100644
--- a/third_party/blink/renderer/core/css/parser/media_query_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/media_query_parser.cc
@@ -402,6 +402,13 @@
     // <mf-boolean> = <mf-name>
     if (!feature_name.IsNull() && stream.AtEnd() &&
         feature_set.IsAllowedWithoutValue(feature_name, execution_context_)) {
+      if (RuntimeEnabledFeatures::CSSCustomMediaEnabled() &&
+          CSSVariableParser::IsValidVariableName(feature_name) &&
+          !feature_set.IsAllowedWithValue(feature_name)) {
+        // custom media query
+        return MakeGarbageCollected<MediaQueryFeatureExpNode>(
+            MediaQueryExp::Create(feature_name));
+      }
       return MakeGarbageCollected<MediaQueryFeatureExpNode>(
           MediaQueryExp::Create(feature_name, MediaQueryExpBounds()));
     }
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index 9855463..4f7bf09 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -4636,10 +4636,6 @@
     case CSSPropertyID::kTimelineTriggerName:
       return css_parsing_utils::ConsumeSingleTimelineTriggerName(stream,
                                                                  context);
-    case CSSPropertyID::kTimelineTriggerBehavior:
-      return css_parsing_utils::ConsumeIdent<
-          CSSValueID::kOnce, CSSValueID::kRepeat, CSSValueID::kAlternate,
-          CSSValueID::kState>(stream);
     case CSSPropertyID::kTimelineTriggerSource:
       return css_parsing_utils::ConsumeAnimationTimeline(stream, context);
     case CSSPropertyID::kTimelineTriggerRangeStart:
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 77350eb..5123d627 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -707,34 +707,6 @@
   return CSSIdentifierValue::Create(CSSValueID::kNone);
 }
 
-const CSSValue* TimelineTriggerBehavior::InitialValue() const {
-  return CSSIdentifierValue::Create(CSSValueID::kOnce);
-}
-
-const CSSValue* TimelineTriggerBehavior::CSSValueFromComputedStyleInternal(
-    const ComputedStyle& style,
-    const LayoutObject*,
-    bool allow_visited_style,
-    CSSValuePhase value_phase) const {
-  return ComputedStyleUtils::ValueForAnimationTriggerBehaviorList(
-      style.Animations()
-          ? style.Animations()->TimelineTriggerBehaviorList()
-          : Vector<EAnimationTriggerBehavior>{
-                CSSAnimationData::InitialTimelineTriggerBehavior()});
-}
-
-const CSSValue* TimelineTriggerBehavior::ParseSingleValue(
-    CSSParserTokenStream& stream,
-    const CSSParserContext&,
-    const CSSParserLocalContext&) const {
-  return css_parsing_utils::ConsumeCommaSeparatedList<CSSIdentifierValue*(
-      CSSParserTokenStream&)>(
-      css_parsing_utils::ConsumeIdent<CSSValueID::kOnce, CSSValueID::kRepeat,
-                                      CSSValueID::kAlternate,
-                                      CSSValueID::kState>,
-      stream);
-}
-
 const CSSValue* TimelineTriggerName::ParseSingleValue(
     CSSParserTokenStream& stream,
     const CSSParserContext& context,
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
index f5124bc2..7e91ca1 100644
--- a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
@@ -4059,9 +4059,6 @@
       list->Append(*ComputedStyleUtils::ValueForAnimationTimeline(
           animation_data->TimelineTriggerSourceList().at(i), style));
 
-      list->Append(*ComputedStyleUtils::ValueForAnimationTriggerBehavior(
-          animation_data->TimelineTriggerBehaviorList().at(i)));
-
       list->Append(*ComputedStyleUtils::ValueForAnimationRange(
           animation_data->TimelineTriggerRangeStartList().at(i), style,
           Length::Percent(0.0)));
@@ -4084,8 +4081,6 @@
   default_list->Append(*CSSIdentifierValue::Create(CSSValueID::kNone));
   default_list->Append(*ComputedStyleUtils::ValueForAnimationTimeline(
       CSSAnimationData::InitialTimelineTriggerSource(), style));
-  default_list->Append(*ComputedStyleUtils::ValueForAnimationTriggerBehavior(
-      CSSAnimationData::InitialTimelineTriggerBehavior()));
   default_list->Append(*ComputedStyleUtils::ValueForAnimationRange(
       CSSAnimationData::InitialTimelineTriggerRangeStart(), style,
       Length::Percent(0.0)));
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 711faf8..11ed25a 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -2459,7 +2459,7 @@
 RuleIndexList* StyleResolver::PseudoCSSRulesForElement(
     Element* element,
     PseudoId pseudo_id,
-    const AtomicString& view_transition_name,
+    const AtomicString& pseudo_argument,
     unsigned rules_to_include) {
   if (!element || !element->isConnected()) {
     return nullptr;
@@ -2472,8 +2472,8 @@
                                  selector_filter_, match_result,
                                  state.ElementLinkState());
   collector.SetMode(SelectorChecker::kCollectingCSSRules);
-  CollectPseudoRulesForElement(*element, collector, pseudo_id,
-                               view_transition_name, rules_to_include);
+  CollectPseudoRulesForElement(*element, collector, pseudo_id, pseudo_argument,
+                               rules_to_include);
 
   if (tracker_) {
     AddMatchedRulesToTracker(collector);
@@ -2491,12 +2491,12 @@
     const Element& element,
     ElementRuleCollector& collector,
     PseudoId pseudo_id,
-    const AtomicString& view_transition_name,
+    const AtomicString& pseudo_argument,
     unsigned rules_to_include) {
   StyleRequest style_request{pseudo_id,
                              /* parent_style */ nullptr,
                              /* originating_element_style */ nullptr,
-                             view_transition_name};
+                             pseudo_argument};
   if (pseudo_id == kPseudoIdSearchText) {
     // TODO(crbug.com/339298411): handle :current?
     style_request.search_text_request = StyleRequest::kNotCurrent;
@@ -2510,7 +2510,7 @@
     if (view_transition_element) {
       auto* view_transition_group_element =
           view_transition_element->GetPseudoElement(
-              kPseudoIdViewTransitionGroup, view_transition_name);
+              kPseudoIdViewTransitionGroup, pseudo_argument);
       if (view_transition_group_element) {
         style_request.pseudo_ident_list =
             To<ViewTransitionPseudoElementBase>(*view_transition_group_element)
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.h b/third_party/blink/renderer/core/css/resolver/style_resolver.h
index 0db7417..1a70791a 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.h
@@ -194,7 +194,7 @@
   RuleIndexList* PseudoCSSRulesForElement(
       Element*,
       PseudoId,
-      const AtomicString& view_transition_name,
+      const AtomicString& pseudo_argument,
       unsigned rules_to_include = kAllCSSRules);
   // Note that StyleRulesForElement will behave as if all links are
   // unvisited; the :visited pseudo-class will never match.
@@ -323,7 +323,7 @@
   void CollectPseudoRulesForElement(const Element&,
                                     ElementRuleCollector&,
                                     PseudoId,
-                                    const AtomicString& view_transition_name,
+                                    const AtomicString& pseudo_argument,
                                     unsigned rules_to_include);
   void MatchUARules(const Element&, ElementRuleCollector&);
   void MatchUserRules(ElementRuleCollector&);
diff --git a/third_party/blink/renderer/core/css/rule_set.cc b/third_party/blink/renderer/core/css/rule_set.cc
index b6a239c..f0bf511 100644
--- a/third_party/blink/renderer/core/css/rule_set.cc
+++ b/third_party/blink/renderer/core/css/rule_set.cc
@@ -1301,6 +1301,10 @@
   // rulesets.
   AddRuleToIntervals(style_scope, rule_count_, scope_intervals_);
   ++rule_count_;
+
+#if DCHECK_IS_ON()
+  all_rules_.push_back(new_rule_data);
+#endif  // DCHECK_IS_ON()
 }
 
 void RuleSet::AddFilteredRulesFromOtherBucket(
diff --git a/third_party/blink/renderer/core/css/rule_set_diff_test.cc b/third_party/blink/renderer/core/css/rule_set_diff_test.cc
new file mode 100644
index 0000000..94c58f1
--- /dev/null
+++ b/third_party/blink/renderer/core/css/rule_set_diff_test.cc
@@ -0,0 +1,41 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/rule_set_diff.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/css_test_helpers.h"
+#include "third_party/blink/renderer/core/css/rule_set.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
+
+namespace blink {
+
+#if DCHECK_IS_ON()
+
+TEST(RuleSetDiffTest, AllRules) {
+  test::TaskEnvironment task_environment;
+
+  css_test_helpers::TestStyleSheet old_sheet;
+  old_sheet.AddCSSRules(".a {}");
+  RuleSet& old_rule_set = old_sheet.GetRuleSet();
+
+  css_test_helpers::TestStyleSheet new_sheet;
+  new_sheet.AddCSSRules(".b {}");
+  RuleSet& new_rule_set = new_sheet.GetRuleSet();
+
+  auto* rule_set_diff = MakeGarbageCollected<RuleSetDiff>(&old_rule_set);
+  base::span<const RuleData> class_rules =
+      old_rule_set.ClassRules(AtomicString("a"));
+  ASSERT_EQ(1u, class_rules.size());
+  rule_set_diff->AddDiff(class_rules.front().Rule());
+  rule_set_diff->NewRuleSetCreated(&new_rule_set);
+
+  RuleSet* diff_ruleset = rule_set_diff->CreateDiffRuleset();
+  ASSERT_TRUE(diff_ruleset);
+  EXPECT_EQ(1u, diff_ruleset->AllRulesForTest().size());
+}
+
+#endif  // DCHECK_IS_ON()
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/selector_checker.cc b/third_party/blink/renderer/core/css/selector_checker.cc
index a4ebba1..c152cfd 100644
--- a/third_party/blink/renderer/core/css/selector_checker.cc
+++ b/third_party/blink/renderer/core/css/selector_checker.cc
@@ -2233,9 +2233,10 @@
         DCHECK((transition->Scope() == &element && context.pseudo_id) ||
                element.IsPseudoElement());
         DCHECK(context.pseudo_argument || element.IsPseudoElement());
-        const AtomicString& pseudo_argument = element.IsPseudoElement()
-                                                  ? element.GetPseudoArgument()
-                                                  : *context.pseudo_argument;
+        const AtomicString& pseudo_argument =
+            element.IsPseudoElement()
+                ? To<PseudoElement>(element).GetPseudoArgument()
+                : *context.pseudo_argument;
         return transition->MatchForOnlyChild(pseudo_id_to_check,
                                              pseudo_argument);
       }
@@ -3147,9 +3148,10 @@
       CHECK(!selector.IdentList().empty());
       const AtomicString& name_or_wildcard = selector.IdentList()[0];
 
-      const String& pseudo_argument = element.IsPseudoElement()
-                                          ? element.GetPseudoArgument()
-                                          : pseudo_argument_;
+      const String& pseudo_argument =
+          element.IsPseudoElement()
+              ? To<PseudoElement>(element).GetPseudoArgument()
+              : pseudo_argument_;
       // note that the pseudo_ident_list is the class list, and
       // pseudo_argument is the name, while in the selector the IdentList() is
       // both the name and the classes.
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 72139deb..79daed3 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -8559,7 +8559,7 @@
   // element in the top layer list.
   if (PseudoElement* backdrop =
           element->GetPseudoElement(PseudoId::kPseudoIdBackdrop,
-                                    /*view_transition_name=*/g_null_atom)) {
+                                    /*pseudo_argument=*/g_null_atom)) {
     CHECK(!backdrop->IsInTopLayer());
     AddToTopLayer(backdrop, element);
   }
@@ -8633,7 +8633,7 @@
   // element in the top layer list.
   if (PseudoElement* backdrop =
           element->GetPseudoElement(PseudoId::kPseudoIdBackdrop,
-                                    /*view_transition_name=*/g_null_atom)) {
+                                    /*pseudo_argument=*/g_null_atom)) {
     CHECK(backdrop->IsInTopLayer());
     RemoveFromTopLayerImmediately(backdrop);
   }
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index f62dd50..20dbd29 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -9333,8 +9333,8 @@
 
 bool Element::ShouldUpdateBackdropPseudoElement(
     const StyleRecalcChange change) {
-  PseudoElement* element = GetPseudoElement(
-      PseudoId::kPseudoIdBackdrop, /* view_transition_name */ g_null_atom);
+  PseudoElement* element = GetPseudoElement(PseudoId::kPseudoIdBackdrop,
+                                            /* pseudo_argument */ g_null_atom);
   bool generate_pseudo = CanGeneratePseudoElement(PseudoId::kPseudoIdBackdrop);
 
   if (element) {
@@ -9371,15 +9371,15 @@
 }
 
 void Element::ApplyPendingBackdropPseudoElementUpdate() {
-  PseudoElement* element = GetPseudoElement(
-      PseudoId::kPseudoIdBackdrop, /* view_transition_name */ g_null_atom);
+  PseudoElement* element = GetPseudoElement(PseudoId::kPseudoIdBackdrop,
+                                            /* pseudo_argument */ g_null_atom);
 
   if (!element && CanGeneratePseudoElement(PseudoId::kPseudoIdBackdrop)) {
     element = PseudoElement::Create(this, PseudoId::kPseudoIdBackdrop,
-                                    /* view_transition_name */ g_null_atom);
-    EnsureElementRareData().SetPseudoElement(
-        PseudoId::kPseudoIdBackdrop, element,
-        /* view_transition_name */ g_null_atom);
+                                    /* pseudo_argument */ g_null_atom);
+    EnsureElementRareData().SetPseudoElement(PseudoId::kPseudoIdBackdrop,
+                                             element,
+                                             /* pseudo_argument */ g_null_atom);
     element->InsertedInto(*this);
     GetDocument().AddToTopLayer(element, this);
   }
@@ -9504,9 +9504,8 @@
 }
 
 void Element::ClearPseudoElement(PseudoId pseudo_id,
-                                 const AtomicString& view_transition_name) {
-  GetElementRareData()->SetPseudoElement(pseudo_id, nullptr,
-                                         view_transition_name);
+                                 const AtomicString& pseudo_argument) {
+  GetElementRareData()->SetPseudoElement(pseudo_id, nullptr, pseudo_argument);
   GetDocument().GetStyleEngine().PseudoElementRemoved(*this);
 }
 
@@ -9563,11 +9562,11 @@
     PseudoId pseudo_id,
     const StyleRecalcChange change,
     const StyleRecalcContext& style_recalc_context,
-    const AtomicString& view_transition_name) {
-  PseudoElement* element = GetPseudoElement(pseudo_id, view_transition_name);
+    const AtomicString& pseudo_argument) {
+  PseudoElement* element = GetPseudoElement(pseudo_id, pseudo_argument);
   if (!element) {
     if ((element = CreatePseudoElementIfNeeded(pseudo_id, style_recalc_context,
-                                               view_transition_name))) {
+                                               pseudo_argument))) {
       // ::before and ::after can have a nested ::marker
       element->CreatePseudoElementIfNeeded(kPseudoIdMarker,
                                            style_recalc_context);
@@ -9606,7 +9605,7 @@
       }
     }
     if (!generate_pseudo) {
-      ClearPseudoElement(pseudo_id, view_transition_name);
+      ClearPseudoElement(pseudo_id, pseudo_argument);
       element = nullptr;
     }
   }
@@ -9617,7 +9616,7 @@
 PseudoElement* Element::CreatePseudoElementIfNeeded(
     PseudoId pseudo_id,
     const StyleRecalcContext& style_recalc_context,
-    const AtomicString& view_transition_name) {
+    const AtomicString& pseudo_argument) {
   if (!CanGeneratePseudoElement(pseudo_id)) {
     return nullptr;
   }
@@ -9633,7 +9632,7 @@
   }
 
   PseudoElement* pseudo_element =
-      PseudoElement::Create(this, pseudo_id, view_transition_name);
+      PseudoElement::Create(this, pseudo_id, pseudo_argument);
   if (RuntimeEnabledFeatures::ScopedViewTransitionsEnabled()) {
     if (!pseudo_element) {
       // TODO(crbug.com/405117185): Replace with DCHECK(pseudo_element) once we
@@ -9642,14 +9641,13 @@
     }
   }
   EnsureElementRareData().SetPseudoElement(pseudo_id, pseudo_element,
-                                           view_transition_name);
+                                           pseudo_argument);
   pseudo_element->InsertedInto(*this);
 
   const ComputedStyle* pseudo_style =
       pseudo_element->StyleForLayoutObject(style_recalc_context);
   if (!PseudoElementLayoutObjectIsNeeded(pseudo_id, pseudo_style, this)) {
-    GetElementRareData()->SetPseudoElement(pseudo_id, nullptr,
-                                           view_transition_name);
+    GetElementRareData()->SetPseudoElement(pseudo_id, nullptr, pseudo_argument);
     // If the content property is relying on attr() we should add the
     // originating element's ComputedStyle to the pseudo-element style cache, so
     // that when attribute value changes it will force style invalidation.
@@ -9703,9 +9701,9 @@
 
 PseudoElement* Element::GetPseudoElement(
     PseudoId pseudo_id,
-    const AtomicString& view_transition_name) const {
+    const AtomicString& pseudo_argument) const {
   if (ElementRareDataVector* data = GetElementRareData()) {
-    return data->GetPseudoElement(pseudo_id, view_transition_name);
+    return data->GetPseudoElement(pseudo_id, pseudo_argument);
   }
   return nullptr;
 }
@@ -9745,10 +9743,9 @@
 
 Element* Element::GetStyledPseudoElement(
     PseudoId pseudo_id,
-    const AtomicString& view_transition_name) const {
+    const AtomicString& pseudo_argument) const {
   if (!IsTransitionPseudoElement(pseudo_id)) {
-    if (PseudoElement* result =
-            GetPseudoElement(pseudo_id, view_transition_name)) {
+    if (PseudoElement* result = GetPseudoElement(pseudo_id, pseudo_argument)) {
       return result;
     }
     const AtomicString& pseudo_string =
@@ -9790,27 +9787,27 @@
 
   auto* container_pseudo =
       To<ViewTransitionTransitionElement>(transition_pseudo)
-          ->FindViewTransitionGroupPseudoElement(view_transition_name);
+          ->FindViewTransitionGroupPseudoElement(pseudo_argument);
   if (!container_pseudo || pseudo_id == kPseudoIdViewTransitionGroup) {
     return container_pseudo;
   }
 
   if (pseudo_id == kPseudoIdViewTransitionGroupChildren) {
-    return container_pseudo->GetPseudoElement(pseudo_id, view_transition_name);
+    return container_pseudo->GetPseudoElement(pseudo_id, pseudo_argument);
   }
 
   auto* wrapper_pseudo = container_pseudo->GetPseudoElement(
-      kPseudoIdViewTransitionImagePair, view_transition_name);
+      kPseudoIdViewTransitionImagePair, pseudo_argument);
   if (!wrapper_pseudo || pseudo_id == kPseudoIdViewTransitionImagePair) {
     return wrapper_pseudo;
   }
 
-  return wrapper_pseudo->GetPseudoElement(pseudo_id, view_transition_name);
+  return wrapper_pseudo->GetPseudoElement(pseudo_id, pseudo_argument);
 }
 
 LayoutObject* Element::PseudoElementLayoutObject(PseudoId pseudo_id) const {
-  if (Element* element = GetStyledPseudoElement(
-          pseudo_id, /*view_transition_name*/ g_null_atom)) {
+  if (Element* element =
+          GetStyledPseudoElement(pseudo_id, /*pseudo_argument*/ g_null_atom)) {
     return element->GetLayoutObject();
   }
   return nullptr;
@@ -11666,20 +11663,8 @@
     }
     for (Member<Element> upstream : AllSourceInterestInvokers(*this)) {
       // This is the target of an interest invoker, which was just de-hovered or
-      // blurred. There are two possibilities:
-      // 1. The upstream invoker is either not an ancestor of this target
-      //    element, or it, too, lost hover/focus. In either case, we need to
-      //    cancel any InterestGained tasks, and schedule an InterestLost task.
-      // 2. The upstream invoker is an ancestor of this target element, we're
-      //    handling a de-hover (not a keyboard blur), and the upstream invoker
-      //    is still hovered. I.e. we moved the mouse off of the target popover
-      //    and back into a descendant of the invoker. In this case, since
-      //    SetFocused() will never be called on the actual invoker, we should
-      //    be careful not to schedule the interestlost task.
-      upstream->GetInvokerData()->CancelInterestGainedTask();
-      if (source == InterestSource::kBlur || !upstream->IsHovered()) {
-        upstream->ScheduleInterestLostTask();
-      }
+      // blurred. Schedule an InterestLost task.
+      upstream->ScheduleInterestLostTask();
     }
   }
 }
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 71f218b..82fd7ac 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -1327,7 +1327,7 @@
   void BeginParsingChildren() { SetIsFinishedParsingChildren(false); }
 
   // Returns the pseudo-element for the given PseudoId type.
-  // |view_transition_name| is used to uniquely identify a pseudo-element
+  // |pseudo_argument| is used to uniquely identify a pseudo-element
   // from a set of pseudo-elements which share the same |pseudo_id|. The current
   // usage of this ID is limited to pseudo-elements generated for a
   // ViewTransition. See
@@ -1336,7 +1336,7 @@
   // Also see GetStyledPseudoElement() below.
   PseudoElement* GetPseudoElement(
       PseudoId,
-      const AtomicString& view_transition_name = g_null_atom) const;
+      const AtomicString& pseudo_argument = g_null_atom) const;
   LayoutObject* PseudoElementLayoutObject(PseudoId) const;
   CSSPseudoElement* pseudo(const AtomicString& type);
 
@@ -1684,7 +1684,7 @@
   std::optional<LayoutUnit> LastRememberedBlockSize() const;
 
   // Returns the element that represents the given |pseudo_id| and
-  // |view_transition_name| originating from this DOM element.  The
+  // |pseudo_argument| originating from this DOM element.  The
   // returned element may be a PseudoElement, or (for element-backed
   // pseudo-elements) an Element.
   //
@@ -1694,9 +1694,8 @@
   //
   // Callers that need to deal with all CSS pseudo-elements should use
   // this rather than GetPseudoElement().
-  Element* GetStyledPseudoElement(
-      PseudoId pseudo_id,
-      const AtomicString& view_transition_name) const;
+  Element* GetStyledPseudoElement(PseudoId pseudo_id,
+                                  const AtomicString& pseudo_argument) const;
 
   // Performs an incremental update of the view-transition pseudo-elements.
   void UpdateTransitionPseudoElements(const StyleRecalcChange,
@@ -1947,9 +1946,8 @@
   void SetElementFlag(ElementFlags, bool value = true);
   void ClearElementFlag(ElementFlags);
 
-  void ClearPseudoElement(
-      PseudoId,
-      const AtomicString& view_transition_name = g_null_atom);
+  void ClearPseudoElement(PseudoId,
+                          const AtomicString& pseudo_argument = g_null_atom);
 
   bool IsElementNode() const =
       delete;  // This will catch anyone doing an unnecessary check.
@@ -2047,7 +2045,7 @@
       PseudoId,
       const StyleRecalcChange,
       const StyleRecalcContext&,
-      const AtomicString& view_transition_name = g_null_atom);
+      const AtomicString& pseudo_argument = g_null_atom);
   enum class StyleUpdatePhase {
     kRecalc,
     kRebuildLayoutTree,
@@ -2069,7 +2067,7 @@
   inline PseudoElement* CreatePseudoElementIfNeeded(
       PseudoId,
       const StyleRecalcContext&,
-      const AtomicString& view_transition_name = g_null_atom);
+      const AtomicString& pseudo_argument = g_null_atom);
 
   // For document element scroll control pseudo-elements become not layout
   // siblings, but layout children.
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc b/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc
index 36cdd63..6c2b3f5 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc
@@ -241,16 +241,17 @@
       // Iterate the list of IDs until we hit the entry for |node's| ID. The
       // sibling is the next ID in the list which generates a pseudo-element.
       bool found = false;
-      for (const auto& view_transition_name :
+      for (const auto& pseudo_argument :
            parent_pseudo->GetContainedViewTransitionNames()) {
         if (!found) {
-          if (view_transition_name == pseudo_element->view_transition_name())
+          if (pseudo_argument == pseudo_element->view_transition_name()) {
             found = true;
+          }
           continue;
         }
 
         if (auto* sibling = parent_element->GetPseudoElement(
-                kPseudoIdViewTransitionGroup, view_transition_name)) {
+                kPseudoIdViewTransitionGroup, pseudo_argument)) {
           return sibling;
         }
       }
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 942ea37..49cf47c 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -501,7 +501,7 @@
       CHECK_EQ(parent->GetPseudoId(), kPseudoIdViewTransitionImagePair);
       return parent->GetPseudoElement(
           kPseudoIdViewTransitionOld,
-          To<PseudoElement>(this)->view_transition_name());
+          To<ViewTransitionPseudoElementBase>(this)->view_transition_name());
     case kPseudoIdViewTransitionGroup: {
       auto* pseudo = To<ViewTransitionPseudoElementBase>(this);
       auto* parent_pseudo = To<ViewTransitionPseudoElementBase>(parent);
@@ -519,7 +519,7 @@
       CHECK_EQ(parent->GetPseudoId(), kPseudoIdViewTransitionGroup);
       return parent->GetPseudoElement(
           kPseudoIdViewTransitionImagePair,
-          To<PseudoElement>(this)->view_transition_name());
+          To<ViewTransitionPseudoElementBase>(this)->view_transition_name());
     case kPseudoIdViewTransitionImagePair:
     case kPseudoIdViewTransitionOld:
       return nullptr;
@@ -628,7 +628,7 @@
       CHECK_EQ(parent->GetPseudoId(), kPseudoIdViewTransitionImagePair);
       return parent->GetPseudoElement(
           kPseudoIdViewTransitionNew,
-          To<PseudoElement>(this)->view_transition_name());
+          To<ViewTransitionPseudoElementBase>(this)->view_transition_name());
     case kPseudoIdViewTransitionGroup: {
       auto* pseudo = To<ViewTransitionPseudoElementBase>(this);
       auto* parent_pseudo = To<ViewTransitionPseudoElementBase>(parent);
@@ -646,7 +646,7 @@
       CHECK_EQ(parent->GetPseudoId(), kPseudoIdViewTransitionGroup);
       return parent->GetPseudoElement(
           kPseudoIdViewTransitionGroupChildren,
-          To<PseudoElement>(this)->view_transition_name());
+          To<ViewTransitionPseudoElementBase>(this)->view_transition_name());
     case kPseudoIdViewTransitionGroupChildren:
     case kPseudoIdViewTransitionNew:
       return nullptr;
@@ -671,11 +671,11 @@
     if (GetPseudoId() == kPseudoIdViewTransitionGroup) {
       return current_element->GetPseudoElement(
           kPseudoIdViewTransitionImagePair,
-          To<PseudoElement>(this)->view_transition_name());
+          To<ViewTransitionPseudoElementBase>(this)->view_transition_name());
     }
     if (GetPseudoId() == kPseudoIdViewTransitionImagePair) {
       const AtomicString& name =
-          To<PseudoElement>(this)->view_transition_name();
+          To<ViewTransitionPseudoElementBase>(this)->view_transition_name();
       if (Node* first = current_element->GetPseudoElement(
               kPseudoIdViewTransitionOld, name)) {
         return first;
@@ -772,16 +772,16 @@
                .empty()) {
         return current_element->GetPseudoElement(
             kPseudoIdViewTransitionGroupChildren,
-            To<PseudoElement>(this)->view_transition_name());
+            To<ViewTransitionPseudoElementBase>(this)->view_transition_name());
       } else {
         return current_element->GetPseudoElement(
             kPseudoIdViewTransitionImagePair,
-            To<PseudoElement>(this)->view_transition_name());
+            To<ViewTransitionPseudoElementBase>(this)->view_transition_name());
       }
     }
     if (GetPseudoId() == kPseudoIdViewTransitionImagePair) {
       const AtomicString& name =
-          To<PseudoElement>(this)->view_transition_name();
+          To<ViewTransitionPseudoElementBase>(this)->view_transition_name();
       if (Node* last = current_element->GetPseudoElement(
               kPseudoIdViewTransitionNew, name)) {
         return last;
@@ -1781,6 +1781,17 @@
   return false;
 }
 
+bool Node::ContainsViaFlatTree(const Node& node) const {
+  const Node* current = &node;
+  do {
+    if (current == this) {
+      return true;
+    }
+    current = FlatTreeTraversal::Parent(*current);
+  } while (current);
+  return false;
+}
+
 Node* Node::CommonAncestor(const Node& other,
                            ContainerNode* (*parent)(const Node&)) const {
   if (this == other)
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h
index 17a3d82f..8d38bd1 100644
--- a/third_party/blink/renderer/core/dom/node.h
+++ b/third_party/blink/renderer/core/dom/node.h
@@ -404,7 +404,6 @@
   }
   virtual PseudoId GetPseudoId() const { return kPseudoIdNone; }
   virtual PseudoId GetPseudoIdForStyling() const { return kPseudoIdNone; }
-  virtual const AtomicString& GetPseudoArgument() const { return g_null_atom; }
 
   CustomElementState GetCustomElementState() const {
     return static_cast<CustomElementState>(node_flags_ &
@@ -760,6 +759,7 @@
   // https://dom.spec.whatwg.org/#concept-shadow-including-ancestor
   bool IsShadowIncludingAncestorOf(const Node&) const;
   bool ContainsIncludingHostElements(const Node&) const;
+  bool ContainsViaFlatTree(const Node&) const;
   Node* CommonAncestor(const Node&,
                        ContainerNode* (*parent)(const Node&)) const;
 
diff --git a/third_party/blink/renderer/core/dom/pseudo_element.cc b/third_party/blink/renderer/core/dom/pseudo_element.cc
index edd6794..e3a1285 100644
--- a/third_party/blink/renderer/core/dom/pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/pseudo_element.cc
@@ -83,7 +83,7 @@
 
 PseudoElement* PseudoElement::Create(Element* parent,
                                      PseudoId pseudo_id,
-                                     const AtomicString& view_transition_name) {
+                                     const AtomicString& pseudo_argument) {
   if (pseudo_id == kPseudoIdCheckMark) {
     if (!IsA<HTMLOptionElement>(parent) && !IsA<HTMLMenuItemElement>(parent)) {
       // The `::checkmark` pseudo-element should only be created for option and
@@ -116,8 +116,7 @@
   } else if (IsTransitionPseudoElement(pseudo_id)) {
     auto* transition = ViewTransitionUtils::GetTransition(*parent);
     DCHECK(transition);
-    return transition->CreatePseudoElement(parent, pseudo_id,
-                                           view_transition_name);
+    return transition->CreatePseudoElement(parent, pseudo_id, pseudo_argument);
   } else if (ResolvePseudoIdAlias(pseudo_id) == kPseudoIdScrollMarkerGroup) {
     return MakeGarbageCollected<ScrollMarkerGroupPseudoElement>(parent,
                                                                 pseudo_id);
@@ -134,7 +133,7 @@
          pseudo_id == kPseudoIdInterestHint || pseudo_id == kPseudoIdBackdrop ||
          pseudo_id == kPseudoIdMarker || pseudo_id == kPseudoIdColumn);
   return MakeGarbageCollected<PseudoElement>(parent, pseudo_id,
-                                             view_transition_name);
+                                             pseudo_argument);
 }
 
 const QualifiedName& PseudoElementTagName(PseudoId pseudo_id) {
@@ -261,7 +260,7 @@
     case kPseudoIdViewTransitionImagePair:
     case kPseudoIdViewTransitionNew:
     case kPseudoIdViewTransitionOld: {
-      auto* pseudo = To<PseudoElement>(element);
+      auto* pseudo = To<ViewTransitionPseudoElementBase>(element);
       DCHECK(pseudo);
       StringBuilder builder;
       builder.Append(PseudoElementTagName(pseudo_id).LocalName());
@@ -297,12 +296,12 @@
 
 PseudoElement::PseudoElement(Element* parent,
                              PseudoId pseudo_id,
-                             const AtomicString& view_transition_name)
+                             const AtomicString& pseudo_argument)
     : Element(PseudoElementTagName(ResolvePseudoIdAlias(pseudo_id)),
               &parent->GetDocument(),
               kCreateElement),
       pseudo_id_(pseudo_id),
-      view_transition_name_(view_transition_name) {
+      pseudo_argument_(pseudo_argument) {
   DCHECK_NE(pseudo_id, kPseudoIdNone);
   parent->GetTreeScope().AdoptIfNeeded(*this);
   SetParentNode(parent);
@@ -342,13 +341,12 @@
         style_recalc_context,
         StyleRequest(kPseudoIdNone, parent->GetComputedStyle(),
                      /* originating_element_style */ nullptr,
-                     view_transition_name_));
+                     pseudo_argument_));
   }
   return parent->StyleForPseudoElement(
       style_recalc_context,
       StyleRequest(GetPseudoIdForStyling(), parent->GetComputedStyle(),
-                   /* originating_element_style */ nullptr,
-                   view_transition_name_));
+                   /* originating_element_style */ nullptr, pseudo_argument_));
 }
 
 // static
diff --git a/third_party/blink/renderer/core/dom/pseudo_element.h b/third_party/blink/renderer/core/dom/pseudo_element.h
index 8712cb90..8d2d306 100644
--- a/third_party/blink/renderer/core/dom/pseudo_element.h
+++ b/third_party/blink/renderer/core/dom/pseudo_element.h
@@ -37,7 +37,7 @@
 
 class CORE_EXPORT PseudoElement : public Element {
  public:
-  // |view_transition_name| is used to uniquely identify a pseudo-element
+  // |pseudo_argument| is used to uniquely identify a pseudo-element
   // from a set of pseudo-elements which share the same |pseudo_id|. The current
   // usage of this ID is limited to pseudo-elements generated for a
   // ViewTransition. See
@@ -45,17 +45,14 @@
   static PseudoElement* Create(
       Element* parent,
       PseudoId pseudo_id,
-      const AtomicString& view_transition_name = g_null_atom);
+      const AtomicString& pseudo_argument = g_null_atom);
 
   PseudoElement(Element*,
                 PseudoId,
-                const AtomicString& view_transition_name = g_null_atom);
+                const AtomicString& pseudo_argument = g_null_atom);
 
   bool IsPseudoElement() const final { return true; }
 
-  const AtomicString& view_transition_name() const {
-    return view_transition_name_;
-  }
   const ComputedStyle* CustomStyleForLayoutObject(
       const StyleRecalcContext&) override;
   void AttachLayoutTree(AttachContext&) override;
@@ -71,9 +68,7 @@
   // unresolved = alias, kPseudoScrollMarkerGroup is resolved.
   // For styling and selector matching, return resolved version.
   PseudoId GetPseudoIdForStyling() const override;
-  const AtomicString& GetPseudoArgument() const override {
-    return view_transition_name_;
-  }
+  const AtomicString& GetPseudoArgument() const { return pseudo_argument_; }
 
   // Return the adjusted style needed by layout. In some cases computed style
   // cannot be used as-is by layout. display:contents needs to be adjusted to
@@ -123,7 +118,7 @@
   };
 
   PseudoId pseudo_id_;
-  const AtomicString view_transition_name_;
+  const AtomicString pseudo_argument_;
   bool is_generated_name_ = false;
 };
 
diff --git a/third_party/blink/renderer/core/dom/pseudo_element_data.h b/third_party/blink/renderer/core/dom/pseudo_element_data.h
index 1c8097c..9123c81 100644
--- a/third_party/blink/renderer/core/dom/pseudo_element_data.h
+++ b/third_party/blink/renderer/core/dom/pseudo_element_data.h
@@ -23,10 +23,10 @@
 
   void SetPseudoElement(PseudoId,
                         PseudoElement*,
-                        const AtomicString& view_transition_name = g_null_atom);
+                        const AtomicString& pseudo_argument = g_null_atom);
   PseudoElement* GetPseudoElement(
       PseudoId,
-      const AtomicString& view_transition_name = g_null_atom) const;
+      const AtomicString& pseudo_argument = g_null_atom) const;
 
   bool HasScrollButtonOrMarkerGroupPseudos() const;
 
@@ -157,7 +157,7 @@
 inline void PseudoElementData::SetPseudoElement(
     PseudoId pseudo_id,
     PseudoElement* element,
-    const AtomicString& view_transition_name) {
+    const AtomicString& pseudo_argument) {
   PseudoElement* previous_element = nullptr;
   switch (pseudo_id) {
     case kPseudoIdCheckMark:
@@ -229,8 +229,7 @@
       if (element && !transition_data_)
         transition_data_ = MakeGarbageCollected<TransitionPseudoElementData>();
       if (transition_data_) {
-        transition_data_->SetPseudoElement(pseudo_id, element,
-                                           view_transition_name);
+        transition_data_->SetPseudoElement(pseudo_id, element, pseudo_argument);
         if (!transition_data_->HasPseudoElements())
           transition_data_ = nullptr;
       }
@@ -245,7 +244,7 @@
 
 inline PseudoElement* PseudoElementData::GetPseudoElement(
     PseudoId pseudo_id,
-    const AtomicString& view_transition_name) const {
+    const AtomicString& pseudo_argument) const {
   if (kPseudoIdCheckMark == pseudo_id) {
     return generated_check_.Get();
   }
@@ -292,9 +291,9 @@
   if (kPseudoIdFirstLetter == pseudo_id)
     return generated_first_letter_.Get();
   if (IsTransitionPseudoElement(pseudo_id)) {
-    return transition_data_ ? transition_data_->GetPseudoElement(
-                                  pseudo_id, view_transition_name)
-                            : nullptr;
+    return transition_data_
+               ? transition_data_->GetPseudoElement(pseudo_id, pseudo_argument)
+               : nullptr;
   }
   return nullptr;
 }
diff --git a/third_party/blink/renderer/core/dom/scroll_marker_pseudo_element.cc b/third_party/blink/renderer/core/dom/scroll_marker_pseudo_element.cc
index 439f687..fd5e786 100644
--- a/third_party/blink/renderer/core/dom/scroll_marker_pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/scroll_marker_pseudo_element.cc
@@ -6,6 +6,7 @@
 
 #include "cc/input/scroll_snap_data.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_scroll_into_view_options.h"
+#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/focus_params.h"
 #include "third_party/blink/renderer/core/dom/scroll_marker_group_pseudo_element.h"
@@ -109,6 +110,19 @@
   }
   is_selected_ = value;
   PseudoStateChanged(CSSSelector::kPseudoTargetCurrent);
+  if (ScrollMarkerGroup()) {
+    const bool tabs_mode = ScrollMarkerGroup()->ScrollMarkerGroupMode() ==
+                           ScrollMarkerGroup::ScrollMarkerMode::kTabs;
+    if (RuntimeEnabledFeatures::CSSScrollMarkerGroupModesEnabled() &&
+        tabs_mode) {
+      // Update accessibility tree. Only active ::scroll-marker's ultimate
+      // originating element and its content are in the tree, when in tabs mode.
+      if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache()) {
+        Element* scroller = ScrollMarkerGroup()->parentElement();
+        cache->RemoveSubtree(scroller);
+      }
+    }
+  }
   if (is_selected_ && scroll_marker_group_) {
     if (LayoutBox* group_box = scroll_marker_group_->GetLayoutBox()) {
       // We defer executing the scroll here in case we are in a lifecycle phase
diff --git a/third_party/blink/renderer/core/dom/text.cc b/third_party/blink/renderer/core/dom/text.cc
index 482e0cf..22a9d08c 100644
--- a/third_party/blink/renderer/core/dom/text.cc
+++ b/third_party/blink/renderer/core/dom/text.cc
@@ -134,21 +134,12 @@
     return nullptr;
 
   if (LayoutText* layout_text = GetLayoutObject()) {
-    if (RuntimeEnabledFeatures::TextDiffSplitFixEnabled()) {
-      // To avoid |LayoutText| has empty text, we rebuild layout tree.
-      if (ContainsOnlyWhitespaceOrEmpty()) {
-        SetForceReattachLayoutTree();
-      } else {
-        layout_text->SetTextWithOffset(
-            data(), TextDiffRange::Delete(offset, old_str.length() - offset));
-      }
+    // To avoid |LayoutText| has empty text, we rebuild layout tree.
+    if (ContainsOnlyWhitespaceOrEmpty()) {
+      SetForceReattachLayoutTree();
     } else {
       layout_text->SetTextWithOffset(
-          data(), TextDiffRange::Delete(0, old_str.length()));
-      if (ContainsOnlyWhitespaceOrEmpty()) {
-        // To avoid |LayoutText| has empty text, we rebuild layout tree.
-        SetForceReattachLayoutTree();
-      }
+          data(), TextDiffRange::Delete(offset, old_str.length() - offset));
     }
   }
 
diff --git a/third_party/blink/renderer/core/dom/transition_pseudo_element_data.h b/third_party/blink/renderer/core/dom/transition_pseudo_element_data.h
index 3582f3c0..480c7f0 100644
--- a/third_party/blink/renderer/core/dom/transition_pseudo_element_data.h
+++ b/third_party/blink/renderer/core/dom/transition_pseudo_element_data.h
@@ -23,10 +23,10 @@
 
   void SetPseudoElement(PseudoId,
                         PseudoElement*,
-                        const AtomicString& view_transition_name = g_null_atom);
+                        const AtomicString& pseudo_argument = g_null_atom);
   PseudoElement* GetPseudoElement(
       PseudoId,
-      const AtomicString& view_transition_name = g_null_atom) const;
+      const AtomicString& pseudo_argument = g_null_atom) const;
 
   void AddPseudoElements(HeapVector<Member<PseudoElement>, 2>* result) const;
 
@@ -60,19 +60,19 @@
   SetPseudoElement(kPseudoIdViewTransition, nullptr);
   SetPseudoElement(kPseudoIdViewTransitionGroupChildren, nullptr,
                    transition_nested_groups_
-                       ? transition_nested_groups_->view_transition_name()
+                       ? transition_nested_groups_->GetPseudoArgument()
                        : g_null_atom);
   SetPseudoElement(kPseudoIdViewTransitionImagePair, nullptr,
                    transition_image_wrapper_
-                       ? transition_image_wrapper_->view_transition_name()
+                       ? transition_image_wrapper_->GetPseudoArgument()
                        : g_null_atom);
   SetPseudoElement(kPseudoIdViewTransitionOld, nullptr,
                    transition_outgoing_image_
-                       ? transition_outgoing_image_->view_transition_name()
+                       ? transition_outgoing_image_->GetPseudoArgument()
                        : g_null_atom);
   SetPseudoElement(kPseudoIdViewTransitionNew, nullptr,
                    transition_incoming_image_
-                       ? transition_incoming_image_->view_transition_name()
+                       ? transition_incoming_image_->GetPseudoArgument()
                        : g_null_atom);
 
   for (auto& entry : transition_containers_)
@@ -91,29 +91,25 @@
       transition_ = element;
       break;
     case kPseudoIdViewTransitionImagePair:
-      DCHECK(!element ||
-             element->view_transition_name() == view_transition_name);
+      DCHECK(!element || element->GetPseudoArgument() == view_transition_name);
       transition_image_wrapper_ = element;
       break;
     case kPseudoIdViewTransitionGroupChildren:
-      DCHECK(!element ||
-             element->view_transition_name() == view_transition_name);
+      DCHECK(!element || element->GetPseudoArgument() == view_transition_name);
       transition_nested_groups_ = element;
       break;
     case kPseudoIdViewTransitionOld:
-      DCHECK(!element ||
-             element->view_transition_name() == view_transition_name);
+      DCHECK(!element || element->GetPseudoArgument() == view_transition_name);
       transition_outgoing_image_ = element;
       break;
     case kPseudoIdViewTransitionNew:
-      DCHECK(!element ||
-             element->view_transition_name() == view_transition_name);
+      DCHECK(!element || element->GetPseudoArgument() == view_transition_name);
       transition_incoming_image_ = element;
       break;
     case kPseudoIdViewTransitionGroup: {
       DCHECK(view_transition_name);
       if (element) {
-        DCHECK_EQ(element->view_transition_name(), view_transition_name);
+        DCHECK_EQ(element->GetPseudoArgument(), view_transition_name);
         transition_containers_.Set(view_transition_name, element);
       } else {
         transition_containers_.erase(view_transition_name);
@@ -136,22 +132,22 @@
       return transition_.Get();
     case kPseudoIdViewTransitionImagePair:
       DCHECK(!transition_image_wrapper_ || !view_transition_name ||
-             transition_image_wrapper_->view_transition_name() ==
+             transition_image_wrapper_->GetPseudoArgument() ==
                  view_transition_name);
       return transition_image_wrapper_.Get();
     case kPseudoIdViewTransitionGroupChildren:
       DCHECK(!transition_nested_groups_ || !view_transition_name ||
-             transition_nested_groups_->view_transition_name() ==
+             transition_nested_groups_->GetPseudoArgument() ==
                  view_transition_name);
       return transition_nested_groups_.Get();
     case kPseudoIdViewTransitionOld:
       DCHECK(!transition_outgoing_image_ || !view_transition_name ||
-             transition_outgoing_image_->view_transition_name() ==
+             transition_outgoing_image_->GetPseudoArgument() ==
                  view_transition_name);
       return transition_outgoing_image_.Get();
     case kPseudoIdViewTransitionNew:
       DCHECK(!transition_incoming_image_ || !view_transition_name ||
-             transition_incoming_image_->view_transition_name() ==
+             transition_incoming_image_->GetPseudoArgument() ==
                  view_transition_name);
       return transition_incoming_image_.Get();
     case kPseudoIdViewTransitionGroup: {
diff --git a/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc b/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc
index 25dfb5e..e4734a0 100644
--- a/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/apply_block_element_command.cc
@@ -337,17 +337,6 @@
     if (end_style->ShouldPreserveBreaks() && start == end &&
         end.OffsetInContainerNode() <
             static_cast<int>(To<Text>(end.ComputeContainerNode())->length())) {
-      if (!RuntimeEnabledFeatures::
-              NoIncreasingEndOffsetOnSplittingTextNodesEnabled()) {
-        int end_offset = end.OffsetInContainerNode();
-        // TODO(yosin) We should use |PositionMoveType::CodePoint| for
-        // |previousPositionOf()|.
-        if (!IsNewLineAtPosition(
-                PreviousPositionOf(end, PositionMoveType::kCodeUnit)) &&
-            IsNewLineAtPosition(end)) {
-          end = Position(end.ComputeContainerNode(), end_offset + 1);
-        }
-      }
       if (is_end_and_end_of_last_paragraph_on_same_node &&
           end.OffsetInContainerNode() >=
               end_of_last_paragraph.OffsetInContainerNode())
diff --git a/third_party/blink/renderer/core/editing/commands/insert_paragraph_separator_command.cc b/third_party/blink/renderer/core/editing/commands/insert_paragraph_separator_command.cc
index e8d0ea5..ca22c13e 100644
--- a/third_party/blink/renderer/core/editing/commands/insert_paragraph_separator_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/insert_paragraph_separator_command.cc
@@ -280,15 +280,13 @@
   if (!start_block || !start_block->NonShadowBoundaryParentNode() ||
       (RuntimeEnabledFeatures::InsertLineBreakIfPhrasingContentEnabled() &&
        IsEditableRootPhrasingContent(insertion_position)) ||
-      (RuntimeEnabledFeatures::InsertLineBreakIfInlineListItemEnabled() &&
-       IsDisplayInlineType(list_child)) ||
-      IsTableCell(start_block) ||
-      IsA<HTMLFormElement>(*start_block)
+      IsDisplayInlineType(list_child) || IsTableCell(start_block) ||
+      IsA<HTMLFormElement>(*start_block) ||
       // FIXME: If the node is hidden, we don't have a canonical position so we
       // will do the wrong thing for tables and <hr>.
       // https://bugs.webkit.org/show_bug.cgi?id=40342
-      || (!canonical_pos.IsNull() &&
-          IsDisplayInsideTable(canonical_pos.AnchorNode())) ||
+      (!canonical_pos.IsNull() &&
+       IsDisplayInsideTable(canonical_pos.AnchorNode())) ||
       (!canonical_pos.IsNull() &&
        IsA<HTMLHRElement>(*canonical_pos.AnchorNode()))) {
     ApplyCommandToComposite(
diff --git a/third_party/blink/renderer/core/events/message_event.cc b/third_party/blink/renderer/core/events/message_event.cc
index 91c2b4b..4213e89 100644
--- a/third_party/blink/renderer/core/events/message_event.cc
+++ b/third_party/blink/renderer/core/events/message_event.cc
@@ -347,8 +347,6 @@
 
     case MessageEvent::kDataTypeSerializedScriptValue:
       if (data_as_serialized_script_value_) {
-        static constexpr size_t kSlowDeserializationSizeThresholdBytes =
-            16 * 1024;
         // The data is put on the V8 GC heap here, and therefore the V8 GC does
         // the accounting from here on. We unregister the registered memory to
         // avoid double accounting.
@@ -359,9 +357,7 @@
         options.slow_mode =
             RuntimeEnabledFeatures::
                 MaskDeserializationTimeForCrossOriginMessagesEnabled() &&
-            data_is_from_untrusted_source_ &&
-            data_as_serialized_script_value_->Value()->DataLengthInBytes() >=
-                kSlowDeserializationSizeThresholdBytes;
+            data_is_from_untrusted_source_;
         value = data_as_serialized_script_value_->Deserialize(isolate, options);
       } else {
         value = v8::Null(isolate);
diff --git a/third_party/blink/renderer/core/events/toggle_event.cc b/third_party/blink/renderer/core/events/toggle_event.cc
index eb650486..c405e2a7 100644
--- a/third_party/blink/renderer/core/events/toggle_event.cc
+++ b/third_party/blink/renderer/core/events/toggle_event.cc
@@ -24,9 +24,6 @@
       old_state_(old_state),
       new_state_(new_state),
       source_(source) {
-  if (!RuntimeEnabledFeatures::ToggleEventSourceEnabled()) {
-    source_ = nullptr;
-  }
   DCHECK(old_state == "closed" || old_state == "open")
       << " old_state should be \"closed\" or \"open\". Was: " << old_state;
   DCHECK(new_state == "closed" || new_state == "open")
@@ -58,7 +55,6 @@
 }
 
 Element* ToggleEvent::source() const {
-  CHECK(RuntimeEnabledFeatures::ToggleEventSourceEnabled());
   if (RuntimeEnabledFeatures::ImprovedSourceRetargetingEnabled()) {
     return Retarget(source_);
   }
diff --git a/third_party/blink/renderer/core/events/toggle_event.idl b/third_party/blink/renderer/core/events/toggle_event.idl
index 98c0260..e50013c1 100644
--- a/third_party/blink/renderer/core/events/toggle_event.idl
+++ b/third_party/blink/renderer/core/events/toggle_event.idl
@@ -8,5 +8,5 @@
     constructor(DOMString type, optional ToggleEventInit eventInitDict = {});
     readonly attribute DOMString oldState;
     readonly attribute DOMString newState;
-    [RuntimeEnabled=ToggleEventSource] readonly attribute Element? source;
+    readonly attribute Element? source;
 };
diff --git a/third_party/blink/renderer/core/events/toggle_event_init.idl b/third_party/blink/renderer/core/events/toggle_event_init.idl
index 1c633c8..2c164ab 100644
--- a/third_party/blink/renderer/core/events/toggle_event_init.idl
+++ b/third_party/blink/renderer/core/events/toggle_event_init.idl
@@ -5,5 +5,5 @@
 dictionary ToggleEventInit : EventInit {
     DOMString oldState = "";
     DOMString newState = "";
-    [RuntimeEnabled=ToggleEventSource] Element? source = null;
+    Element? source = null;
 };
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h
index 0da4a06c..db3b20c 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.h
+++ b/third_party/blink/renderer/core/execution_context/execution_context.h
@@ -256,12 +256,6 @@
   void SetLifecycleState(mojom::FrameLifecycleState);
   virtual void NotifyContextDestroyed();
 
-  using ConsoleLogger::AddConsoleMessage;
-
-  void AddConsoleMessage(ConsoleMessage* message,
-                         bool discard_duplicates = false) {
-    AddConsoleMessageImpl(message, discard_duplicates);
-  }
   virtual void AddInspectorIssue(AuditsIssue) = 0;
 
   void CountDeprecation(WebFeature feature) override;
diff --git a/third_party/blink/renderer/core/exported/web_dom_message_event.cc b/third_party/blink/renderer/core/exported/web_dom_message_event.cc
index bc386f9..1c81e551 100644
--- a/third_party/blink/renderer/core/exported/web_dom_message_event.cc
+++ b/third_party/blink/renderer/core/exported/web_dom_message_event.cc
@@ -31,47 +31,22 @@
 #include "third_party/blink/public/web/web_dom_message_event.h"
 
 #include "third_party/blink/public/mojom/messaging/delegated_capability.mojom-blink.h"
-#include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/public/web/web_document.h"
-#include "third_party/blink/public/web/web_frame.h"
 #include "third_party/blink/public/web/web_serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
-#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/event_type_names.h"
 #include "third_party/blink/renderer/core/events/message_event.h"
-#include "third_party/blink/renderer/core/frame/local_dom_window.h"
-#include "third_party/blink/renderer/core/frame/user_activation.h"
-#include "third_party/blink/renderer/core/messaging/blink_transferable_message.h"
-#include "third_party/blink/renderer/core/messaging/message_port.h"
 
 namespace blink {
 
 WebDOMMessageEvent::WebDOMMessageEvent(
-    const WebSerializedScriptValue& message_data,
-    const WebString& origin,
-    const WebFrame* source_frame,
-    const WebDocument& target_document,
-    std::vector<MessagePortChannel> channels)
+    const WebSerializedScriptValue& message_data)
     : WebDOMMessageEvent(MessageEvent::Create()) {
-  DOMWindow* window = nullptr;
-  if (source_frame)
-    window = WebFrame::ToCoreFrame(*source_frame)->DomWindow();
-  GCedMessagePortArray* ports = nullptr;
-  if (!target_document.IsNull()) {
-    Document* core_document = target_document;
-    ports = MessagePort::EntanglePorts(*core_document->GetExecutionContext(),
-                                       std::move(channels));
-  }
   // TODO(esprehn): Chromium always passes empty string for lastEventId, is that
   // right?
   Unwrap<MessageEvent>()->initMessageEvent(
-      event_type_names::kMessage, false, false, message_data, origin,
-      MessageEvent::kMessageIsSameOrigin, "" /*lastEventId*/, window, ports,
+      event_type_names::kMessage, false, false, message_data, "",
+      MessageEvent::kMessageIsSameOrigin, "" /*lastEventId*/, nullptr, {},
       nullptr /*user_activation*/, mojom::blink::DelegatedCapability::kNone);
 }
 
-WebString WebDOMMessageEvent::Origin() {
-  return WebString(Unwrap<MessageEvent>()->origin());
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_node.cc b/third_party/blink/renderer/core/exported/web_node.cc
index f8d5b0e..466236e 100644
--- a/third_party/blink/renderer/core/exported/web_node.cc
+++ b/third_party/blink/renderer/core/exported/web_node.cc
@@ -100,8 +100,8 @@
   return private_->contains(n->private_.Get());
 }
 
-bool WebNode::ContainsIncludingHostElements(const WebNode* n) const {
-  return private_->ContainsIncludingHostElements(*n->private_.Get());
+bool WebNode::ContainsViaFlatTree(const WebNode* n) const {
+  return private_->ContainsViaFlatTree(*n->private_.Get());
 }
 
 WebNode WebNode::ParentNode() const {
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
index ba583e7..3196381 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
@@ -231,7 +231,8 @@
     CrossVariantMojoReceiver<mojom::blink::ReportingObserverInterfaceBase>
         coep_reporting_observer,
     CrossVariantMojoReceiver<mojom::blink::ReportingObserverInterfaceBase>
-        dip_reporting_observer) {
+        dip_reporting_observer,
+    std::optional<blink::NoiseToken> canvas_noise_token) {
   DCHECK(IsMainThread());
   DCHECK(web_worker_fetch_context);
   CHECK(constructor_origin.Get()->CanAccessSharedWorkers());
@@ -294,7 +295,8 @@
       require_cross_site_request_for_cookies,
       blink::SecurityOrigin::CreateFromUrlOrigin(
           url::Origin(origin_from_browser)),
-      std::move(coep_reporting_observer), std::move(dip_reporting_observer));
+      std::move(coep_reporting_observer), std::move(dip_reporting_observer),
+      std::move(canvas_noise_token));
 
   auto thread_startup_data = WorkerBackingThreadStartupData::CreateDefault();
   thread_startup_data.atomics_wait_mode =
@@ -380,7 +382,8 @@
     CrossVariantMojoReceiver<mojom::blink::ReportingObserverInterfaceBase>
         coep_reporting_observer,
     CrossVariantMojoReceiver<mojom::blink::ReportingObserverInterfaceBase>
-        dip_reporting_observer) {
+        dip_reporting_observer,
+    std::optional<blink::NoiseToken> canvas_noise_token) {
   auto worker =
       base::WrapUnique(new WebSharedWorkerImpl(token, std::move(host), client));
   worker->StartWorkerContext(
@@ -392,7 +395,8 @@
       pause_worker_context_on_start, std::move(worker_main_script_load_params),
       std::move(policy_container), std::move(web_worker_fetch_context),
       ukm_source_id, require_cross_site_request_for_cookies,
-      std::move(coep_reporting_observer), std::move(dip_reporting_observer));
+      std::move(coep_reporting_observer), std::move(dip_reporting_observer),
+      std::move(canvas_noise_token));
   return worker;
 }
 
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
index 5268705..3ca675739 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
@@ -41,6 +41,7 @@
 #include "services/network/public/mojom/content_security_policy.mojom-blink-forward.h"
 #include "services/network/public/mojom/fetch_api.mojom-shared.h"
 #include "services/network/public/mojom/source_location.mojom-blink.h"
+#include "third_party/blink/public/common/fingerprinting_protection/noise_token.h"
 #include "third_party/blink/public/mojom/browser_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame/reporting_observer.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/script/script_type.mojom-blink.h"
@@ -131,7 +132,8 @@
       CrossVariantMojoReceiver<mojom::blink::ReportingObserverInterfaceBase>
           coep_reporting_observer,
       CrossVariantMojoReceiver<mojom::blink::ReportingObserverInterfaceBase>
-          dip_reporting_observer);
+          dip_reporting_observer,
+      std::optional<blink::NoiseToken> canvas_noise_token);
 
   void DispatchPendingConnections();
   void ConnectToChannel(int connection_request_id,
diff --git a/third_party/blink/renderer/core/fetch/window_fetch.idl b/third_party/blink/renderer/core/fetch/window_or_worker_global_scope_fetch.idl
similarity index 86%
rename from third_party/blink/renderer/core/fetch/window_fetch.idl
rename to third_party/blink/renderer/core/fetch/window_or_worker_global_scope_fetch.idl
index ed6ff17..70e170c7 100644
--- a/third_party/blink/renderer/core/fetch/window_fetch.idl
+++ b/third_party/blink/renderer/core/fetch/window_or_worker_global_scope_fetch.idl
@@ -6,6 +6,6 @@
 
 [
     ImplementedAs=GlobalFetch
-] partial interface Window {
+] partial interface mixin WindowOrWorkerGlobalScope {
     [CallWith=ScriptState, NewObject, RaisesException] Promise<Response> fetch(RequestInfo input, optional RequestInit init = {});
 };
diff --git a/third_party/blink/renderer/core/fetch/worker_fetch.idl b/third_party/blink/renderer/core/fetch/worker_fetch.idl
deleted file mode 100644
index 346a018..0000000
--- a/third_party/blink/renderer/core/fetch/worker_fetch.idl
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2014 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// https://fetch.spec.whatwg.org/#fetch-method
-
-[
-    ImplementedAs=GlobalFetch
-] partial interface WorkerGlobalScope {
-    [CallWith=ScriptState, NewObject, RaisesException] Promise<Response> fetch(RequestInfo input, optional RequestInit init = {});
-};
diff --git a/third_party/blink/renderer/core/frame/dom_window.cc b/third_party/blink/renderer/core/frame/dom_window.cc
index 9736ada..29117dc 100644
--- a/third_party/blink/renderer/core/frame/dom_window.cc
+++ b/third_party/blink/renderer/core/frame/dom_window.cc
@@ -1024,16 +1024,6 @@
       UseCounter::Count(source, WebFeature::kCrossSitePostMessage);
     }
   }
-  auto* local_dom_window = DynamicTo<LocalDOMWindow>(this);
-  KURL target_url = local_dom_window
-                        ? local_dom_window->Url()
-                        : KURL(NullURL(), target_security_origin->ToString());
-  if (!source->GetContentSecurityPolicy()->AllowConnectToSource(
-          target_url, target_url, RedirectStatus::kNoRedirect,
-          ReportingDisposition::kSuppressReporting)) {
-    UseCounter::Count(
-        source, WebFeature::kPostMessageOutgoingWouldBeBlockedByConnectSrc);
-  }
   UserActivation* user_activation = nullptr;
   if (options->includeUserActivation())
     user_activation = UserActivation::CreateSnapshot(source);
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index 0dcd971d..2e4dc6e 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -1357,17 +1357,9 @@
     }
   }
 
-  KURL sender(event->origin());
-  if (!GetContentSecurityPolicy()->AllowConnectToSource(
-          sender, sender, RedirectStatus::kNoRedirect,
-          ReportingDisposition::kSuppressReporting)) {
-    UseCounter::Count(
-        this, WebFeature::kPostMessageIncomingWouldBeBlockedByConnectSrc);
-  }
-
   if (event->IsOriginCheckRequiredToAccessData()) {
     scoped_refptr<SecurityOrigin> sender_security_origin =
-        SecurityOrigin::Create(sender);
+        SecurityOrigin::CreateFromString(event->origin());
     if (!sender_security_origin->IsSameOriginWith(GetSecurityOrigin())) {
       event = MessageEvent::CreateError(event->origin(), event->source());
     }
@@ -1380,7 +1372,7 @@
       event = MessageEvent::CreateError(event->origin(), event->source());
     } else {
       scoped_refptr<SecurityOrigin> sender_origin =
-          SecurityOrigin::Create(sender);
+          SecurityOrigin::CreateFromString(event->origin());
       if (!sender_origin->IsSameOriginWith(GetSecurityOrigin())) {
         UseCounter::Count(
             this, WebFeature::kMessageEventSharedArrayBufferSameAgentCluster);
diff --git a/third_party/blink/renderer/core/geolocation/geolocation.cc b/third_party/blink/renderer/core/geolocation/geolocation.cc
index 23aef2c7..d3d4c697 100644
--- a/third_party/blink/renderer/core/geolocation/geolocation.cc
+++ b/third_party/blink/renderer/core/geolocation/geolocation.cc
@@ -210,9 +210,10 @@
   }
 }
 
-void Geolocation::getCurrentPosition(V8PositionCallback* success_callback,
-                                     V8PositionErrorCallback* error_callback,
-                                     const PositionOptions* options) {
+void Geolocation::getCurrentPositionForBindings(
+    V8PositionCallback* success_callback,
+    V8PositionErrorCallback* error_callback,
+    const PositionOptions* options) {
   if (options->enableHighAccuracy()) {
     UseCounter::Count(GetExecutionContext(),
                       WebFeature::kGeolocationGetCurrentPositionHighAccuracy);
@@ -239,9 +240,23 @@
   StartRequest(notifier);
 }
 
-int Geolocation::watchPosition(V8PositionCallback* success_callback,
-                               V8PositionErrorCallback* error_callback,
-                               const PositionOptions* options) {
+void Geolocation::GetCurrentPosition(
+    base::RepeatingCallback<
+        void(base::expected<Geoposition*, GeolocationPositionError*>)> callback,
+    const PositionOptions* options) {
+  if (!GetFrame()) {
+    return;
+  }
+  auto* notifier =
+      MakeGarbageCollected<GeoNotifierBlink>(this, options, callback);
+  one_shots_->insert(notifier);
+  StartRequest(notifier);
+}
+
+int Geolocation::watchPositionForBindings(
+    V8PositionCallback* success_callback,
+    V8PositionErrorCallback* error_callback,
+    const PositionOptions* options) {
   if (options->enableHighAccuracy()) {
     UseCounter::Count(GetExecutionContext(),
                       WebFeature::kGeolocationGetCurrentPositionHighAccuracy);
@@ -260,32 +275,32 @@
     UseCounter::Count(GetExecutionContext(),
                       WebFeature::kAdScriptInStackOnWatchGeoLocation);
   }
+  return WatchPositionInternal(notifier);
+}
 
+int Geolocation::WatchPosition(
+    base::RepeatingCallback<
+        void(base::expected<Geoposition*, GeolocationPositionError*>)> callback,
+    const PositionOptions* options) {
+  if (!GetFrame()) {
+    return 0;
+  }
+  auto* notifier =
+      MakeGarbageCollected<GeoNotifierBlink>(this, options, callback);
+  return WatchPositionInternal(notifier);
+}
+
+int Geolocation::WatchPositionInternal(GeoNotifier* notifier) {
   int watch_id;
   // Keep asking for the next id until we're given one that we don't already
   // have.
   do {
     watch_id = GetExecutionContext()->CircularSequentialID();
   } while (!watchers_->Add(watch_id, notifier));
-
   StartRequest(notifier);
-
   return watch_id;
 }
 
-void Geolocation::RequestPosition(
-    base::RepeatingCallback<
-        void(base::expected<Geoposition*, GeolocationPositionError*>)> callback,
-    const PositionOptions* options) {
-  if (!GetFrame()) {
-    return;
-  }
-  auto* notifier =
-      MakeGarbageCollected<GeoNotifierBlink>(this, options, callback);
-  one_shots_->insert(notifier);
-  StartRequest(notifier);
-}
-
 void Geolocation::StartRequest(GeoNotifier* notifier) {
   RecordOriginTypeAccess();
   String error_message;
diff --git a/third_party/blink/renderer/core/geolocation/geolocation.h b/third_party/blink/renderer/core/geolocation/geolocation.h
index 5c910d8..a340557 100644
--- a/third_party/blink/renderer/core/geolocation/geolocation.h
+++ b/third_party/blink/renderer/core/geolocation/geolocation.h
@@ -82,28 +82,42 @@
 
   // Creates a oneshot and attempts to obtain a position that meets the
   // constraints of the options. This method gets called when the geolocation
-  // API is invoked from V8.
-  void getCurrentPosition(V8PositionCallback*,
-                          V8PositionErrorCallback* = nullptr,
-                          const PositionOptions* = PositionOptions::Create());
-
-  // Creates a watcher that will be notified whenever a new position is
-  // available that meets the constraints of the options.
-  int watchPosition(V8PositionCallback*,
-                    V8PositionErrorCallback* = nullptr,
-                    const PositionOptions* = PositionOptions::Create());
-
-  // Removes all references to the watcher, it will not be updated again.
-  void clearWatch(int watch_id);
+  // API is invoked from script.
+  void getCurrentPositionForBindings(
+      V8PositionCallback*,
+      V8PositionErrorCallback* = nullptr,
+      const PositionOptions* = PositionOptions::Create());
 
   // Creates a oneshot and attempts to obtain a position that meets the
   // constraints of the options. This method gets called when the geolocation
   // API is invoked from Blink.
-  void RequestPosition(
+  void GetCurrentPosition(
       base::RepeatingCallback<
           void(base::expected<Geoposition*, GeolocationPositionError*>)>,
       const PositionOptions* = PositionOptions::Create());
 
+  // Creates a watcher that will be notified whenever a new position is
+  // available that meets the constraints of the options. This method gets
+  // called when the geolocation API is invoked from script.
+  int watchPositionForBindings(
+      V8PositionCallback*,
+      V8PositionErrorCallback* = nullptr,
+      const PositionOptions* = PositionOptions::Create());
+
+  // Creates a watcher that will be notified whenever a new position is
+  // available that meets the constraints of the options. This method gets
+  // called when the geolocation API is invoked from Blink.
+  // Returns an watch ID, which can be used to clear the watcher.
+  int WatchPosition(
+      base::RepeatingCallback<
+          void(base::expected<Geoposition*, GeolocationPositionError*>)>,
+      const PositionOptions* = PositionOptions::Create());
+
+  int WatchPositionInternal(GeoNotifier* notifier);
+
+  // Removes all references to the watcher, it will not be updated again.
+  void clearWatch(int watch_id);
+
   // Notifies this that a new position is available. Must never be called
   // before permission is granted by the user.
   void PositionChanged();
diff --git a/third_party/blink/renderer/core/geolocation/geolocation.idl b/third_party/blink/renderer/core/geolocation/geolocation.idl
index 572c776..6d75074 100644
--- a/third_party/blink/renderer/core/geolocation/geolocation.idl
+++ b/third_party/blink/renderer/core/geolocation/geolocation.idl
@@ -30,7 +30,8 @@
 ] interface Geolocation {
     [
         LogActivity,
-        MeasureAs=GeolocationGetCurrentPosition
+        MeasureAs=GeolocationGetCurrentPosition,
+        ImplementedAs=getCurrentPositionForBindings
     ] void getCurrentPosition(
         PositionCallback successCallback,
         optional PositionErrorCallback? errorCallback = null,
@@ -38,7 +39,8 @@
 
     [
         LogActivity,
-        MeasureAs=GeolocationWatchPosition
+        MeasureAs=GeolocationWatchPosition,
+        ImplementedAs=watchPositionForBindings
     ] long watchPosition(
         PositionCallback successCallback,
         optional PositionErrorCallback? errorCallback = null,
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.cc b/third_party/blink/renderer/core/html/forms/html_form_element.cc
index 2e64646..68c11f8 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.cc
@@ -417,18 +417,10 @@
 }
 
 void HTMLFormElement::SubmitDialog(FormSubmission* form_submission) {
-  if (RuntimeEnabledFeatures::DialogSubmitShadowBoundariesEnabled()) {
-    if (auto* dialog = Traversal<HTMLDialogElement>::FirstAncestor(*this)) {
-      dialog->close(form_submission->Result());
-    }
-    return;
+  if (auto* dialog = Traversal<HTMLDialogElement>::FirstAncestor(*this)) {
+    dialog->close(form_submission->Result());
   }
-  for (Node* node = this; node; node = node->ParentOrShadowHostNode()) {
-    if (auto* dialog = DynamicTo<HTMLDialogElement>(*node)) {
-      dialog->close(form_submission->Result());
-      return;
-    }
-  }
+  return;
 }
 
 void HTMLFormElement::ScheduleFormSubmission(
diff --git a/third_party/blink/renderer/core/html/forms/html_option_element.cc b/third_party/blink/renderer/core/html/forms/html_option_element.cc
index cf52532..315fde2 100644
--- a/third_party/blink/renderer/core/html/forms/html_option_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_option_element.cc
@@ -205,27 +205,15 @@
 // content should be kept in sync with the ::-internal-option-label-container
 // rules in the UA stylesheet.
 String HTMLOptionElement::DisplayLabel() const {
-  if (RuntimeEnabledFeatures::OptionLabelAttributeWhitespaceEnabled()) {
-    // If the label attribute is set and is not an empty string, then use its
-    // value. Otherwise, use inner text.
-    String label_attr = String(FastGetAttribute(html_names::kLabelAttr));
-    if (!label_attr.empty()) {
-      return label_attr;
-    }
-    return CollectOptionInnerText()
-        .StripWhiteSpace(IsHTMLSpace<UChar>)
-        .SimplifyWhiteSpace(IsHTMLSpace<UChar>);
+  // If the label attribute is set and is not an empty string, then use its
+  // value. Otherwise, use inner text.
+  String label_attr = String(FastGetAttribute(html_names::kLabelAttr));
+  if (!label_attr.empty()) {
+    return label_attr;
   }
-
-  String label_attr = String(FastGetAttribute(html_names::kLabelAttr))
-    .StripWhiteSpace(IsHTMLSpace<UChar>).SimplifyWhiteSpace(IsHTMLSpace<UChar>);
-  String inner_text = CollectOptionInnerText()
-    .StripWhiteSpace(IsHTMLSpace<UChar>).SimplifyWhiteSpace(IsHTMLSpace<UChar>);
-  // FIXME: The following treats an element with the label attribute set to
-  // the empty string the same as an element with no label attribute at all.
-  // Is that correct? If it is, then should the label function work the same
-  // way?
-  return label_attr.empty() ? inner_text : label_attr;
+  return CollectOptionInnerText()
+      .StripWhiteSpace(IsHTMLSpace<UChar>)
+      .SimplifyWhiteSpace(IsHTMLSpace<UChar>);
 }
 
 String HTMLOptionElement::text() const {
diff --git a/third_party/blink/renderer/core/html/forms/listed_element.cc b/third_party/blink/renderer/core/html/forms/listed_element.cc
index 0d441c7..079ebb5 100644
--- a/third_party/blink/renderer/core/html/forms/listed_element.cc
+++ b/third_party/blink/renderer/core/html/forms/listed_element.cc
@@ -479,15 +479,11 @@
 }
 
 void ListedElement::SetCustomValidationMessage(const String& message) {
-  if (RuntimeEnabledFeatures::CustomValidityNormalizeNewlinesEnabled()) {
-    // \r\n and \r should be replaced with \n:
-    // https://github.com/whatwg/html/pull/10350
-    String message_copy(message);
-    custom_validation_message_ =
-        message_copy.Replace("\r\n", "\n").Replace('\r', '\n');
-  } else {
-    custom_validation_message_ = message;
-  }
+  // \r\n and \r should be replaced with \n:
+  // https://github.com/whatwg/html/pull/10350.
+  String message_copy(message);
+  custom_validation_message_ =
+      message_copy.Replace("\r\n", "\n").Replace('\r', '\n');
 }
 
 String ListedElement::validationMessage() const {
diff --git a/third_party/blink/renderer/core/html/forms/select_type.cc b/third_party/blink/renderer/core/html/forms/select_type.cc
index 24082f1..715ea7da 100644
--- a/third_party/blink/renderer/core/html/forms/select_type.cc
+++ b/third_party/blink/renderer/core/html/forms/select_type.cc
@@ -529,14 +529,6 @@
     const KeyboardEvent& event) {
   LayoutTheme& layout_theme = LayoutTheme::GetTheme();
   int key_code = event.keyCode();
-
-  // TODO(crbug.com/1511354): Reconsider making appearance:base-select affect
-  // keyboard behavior after a resolution here:
-  // https://github.com/openui/open-ui/issues/1087
-  if (IsAppearanceBase() && key_code == '\r') {
-    return false;
-  }
-
   return ((key_code == ' ' && !select_->type_ahead_.HasActiveSession(event)) ||
           (layout_theme.PopsMenuByReturnKey() && key_code == '\r'));
 }
diff --git a/third_party/blink/renderer/core/html/html_details_element.cc b/third_party/blink/renderer/core/html/html_details_element.cc
index 268ec9e..532f9aa 100644
--- a/third_party/blink/renderer/core/html/html_details_element.cc
+++ b/third_party/blink/renderer/core/html/html_details_element.cc
@@ -325,8 +325,7 @@
     return false;
   }
 
-  if (RuntimeEnabledFeatures::ToggleEventSourceEnabled() &&
-      pending_toggle_event_) {
+  if (pending_toggle_event_) {
     // pending_toggle_event_ is created inside the attribute handling code which
     // we can't pass the invoker to, so we set it here instead.
     pending_toggle_event_ = ToggleEvent::Create(
diff --git a/third_party/blink/renderer/core/html/html_geolocation_element.cc b/third_party/blink/renderer/core/html/html_geolocation_element.cc
index 15e0a8d..68fc070 100644
--- a/third_party/blink/renderer/core/html/html_geolocation_element.cc
+++ b/third_party/blink/renderer/core/html/html_geolocation_element.cc
@@ -38,7 +38,9 @@
   HTMLPermissionElement::Trace(visitor);
 }
 
-void HTMLGeolocationElement::UpdateText() {
+void HTMLGeolocationElement::UpdateAppearance() {
+  UpdateIcon(mojom::blink::PermissionName::GEOLOCATION);
+
   // TODO(crbug.com/435376388): There will be more strings related to location
   // data querying, for example: Sending location.
   uint16_t message_id = GetTranslatedMessageID(
@@ -66,31 +68,55 @@
   return descriptor;
 }
 
+Geolocation* HTMLGeolocationElement::GetGeolocation() {
+  auto* dom_window = GetDocument().domWindow();
+  if (!dom_window) {
+    return nullptr;
+  }
+  return Geolocation::geolocation(*dom_window->navigator());
+}
+
 void HTMLGeolocationElement::AttributeChanged(
     const AttributeModificationParams& params) {
-  // The autolocate attribute is exclusive to the geolocation element, other
-  // attributes will be handled by the HTMLPermissionElement.
+  // The autolocate and the watch attributes are exclusive to the geolocation
+  // element, other attributes will be handled by the HTMLPermissionElement.
   if (params.name == html_names::kAutolocateAttr) {
     GetCurrentPosition();
   }
+  if (params.name == html_names::kWatchAttr) {
+    if (params.new_value) {
+      WatchPosition();
+    } else {
+      auto* geolocation = GetGeolocation();
+      if (!geolocation) {
+        return;
+      }
+      geolocation->clearWatch(watch_id_);
+      watch_id_ = 0;
+    }
+  }
   HTMLPermissionElement::AttributeChanged(params);
 }
 
 void HTMLGeolocationElement::GetCurrentPosition() {
-  auto* dom_window = GetDocument().domWindow();
-  if (!dom_window) {
-    return;
-  }
-  auto* geolocation = Geolocation::geolocation(*dom_window->navigator());
+  auto* geolocation = GetGeolocation();
   if (!geolocation) {
     return;
   }
-  // TODO(@ravjit): Discuss with the team how much timeout we should set.
-  geolocation->RequestPosition(
+  geolocation->GetCurrentPosition(
       blink::BindRepeating(&HTMLGeolocationElement::CurrentPositionCallback,
                            WrapWeakPersistent(this)));
 }
 
+void HTMLGeolocationElement::WatchPosition() {
+  auto* geolocation = GetGeolocation();
+  if (!geolocation) {
+    return;
+  }
+  watch_id_ = geolocation->WatchPosition(
+      blink::BindRepeating(&HTMLGeolocationElement::CurrentPositionCallback,
+                           WrapWeakPersistent(this)));
+}
 void HTMLGeolocationElement::CurrentPositionCallback(
     base::expected<Geoposition*, GeolocationPositionError*> position) {
   if (position.has_value()) {
diff --git a/third_party/blink/renderer/core/html/html_geolocation_element.h b/third_party/blink/renderer/core/html/html_geolocation_element.h
index a1c8dfc..be69e83b 100644
--- a/third_party/blink/renderer/core/html/html_geolocation_element.h
+++ b/third_party/blink/renderer/core/html/html_geolocation_element.h
@@ -7,6 +7,7 @@
 
 #include "base/types/expected.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
+#include "third_party/blink/renderer/core/geolocation/geolocation.h"
 #include "third_party/blink/renderer/core/geolocation/geolocation_position_error.h"
 #include "third_party/blink/renderer/core/geolocation/geolocation_watchers.h"
 #include "third_party/blink/renderer/core/geolocation/geoposition.h"
@@ -38,7 +39,7 @@
   void Trace(Visitor*) const override;
 
   // HTMLPermissionElement:
-  void UpdateText() override;
+  void UpdateAppearance() override;
   void UpdatePermissionStatusAndAppearance() override;
   mojom::blink::EmbeddedPermissionRequestDescriptorPtr
   CreateEmbeddedPermissionRequestDescriptor() override;
@@ -47,13 +48,19 @@
   // blink::HTMLPermissionElement:
   void AttributeChanged(const AttributeModificationParams& params) override;
   void GetCurrentPosition();
+  void WatchPosition();
   // Callback for Geolocation::getCurrentPosition. It is called when the
   // geolocation API returns a position or an error.
   void CurrentPositionCallback(
       base::expected<Geoposition*, GeolocationPositionError*>);
+  Geolocation* GetGeolocation();
+
   bool precise_ = false;
   bool autolocate_ = false;
   bool watch_ = false;
+  // The watch_id_ is used to identify the watcher in the Geolocation object.
+  // The ids always start from 1. 0 means that the watch is not set.
+  int watch_id_ = 0;
 
   Member<Geoposition> position_;
   Member<GeolocationPositionError> error_;
diff --git a/third_party/blink/renderer/core/html/html_permission_element.cc b/third_party/blink/renderer/core/html/html_permission_element.cc
index e2a147a..10167f0 100644
--- a/third_party/blink/renderer/core/html/html_permission_element.cc
+++ b/third_party/blink/renderer/core/html/html_permission_element.cc
@@ -586,7 +586,7 @@
       .value_or(message_id);
 }
 
-void HTMLPermissionElement::UpdateText() {
+void HTMLPermissionElement::UpdateAppearance() {
   bool permission_granted;
   PermissionName permission_name;
   wtf_size_t permission_count;
@@ -604,16 +604,10 @@
     permission_name = permission_status_map_.begin()->key;
     permission_count = permission_status_map_.size();
   }
-  if (RuntimeEnabledFeatures::PermissionElementIconEnabled(
-          GetDocument().GetExecutionContext())) {
-    GetTaskRunner()->PostTask(
-        FROM_HERE,
-        BindOnce(&HTMLPermissionIconElement::SetIcon,
-                 WrapWeakPersistent(permission_internal_icon_.Get()),
-                 permission_count == 1 ? permission_name
-                                       : PermissionName::VIDEO_CAPTURE,
-                 is_precise_location_));
-  }
+
+  UpdateIcon(permission_count == 1 ? permission_name
+                                   : PermissionName::VIDEO_CAPTURE);
+
   AtomicString language_string = ComputeInheritedLanguage().LowerASCII();
 
   uint16_t untranslated_message_id =
@@ -628,10 +622,19 @@
       GetLocale().QueryString(translated_message_id));
 }
 
+void HTMLPermissionElement::UpdateIcon(PermissionName permnission) {
+  if (!RuntimeEnabledFeatures::PermissionElementIconEnabled(
+          GetDocument().GetExecutionContext())) {
+    return;
+  }
+
+  permission_internal_icon_->SetIcon(permnission, is_precise_location_);
+}
+
 void HTMLPermissionElement::UpdatePermissionStatusAndAppearance() {
   UpdatePermissionStatus();
   PseudoStateChanged(CSSSelector::kPseudoPermissionGranted);
-  UpdateText();
+  UpdateAppearance();
 }
 
 mojom::blink::EmbeddedPermissionRequestDescriptorPtr
@@ -808,7 +811,7 @@
 }
 
 void HTMLPermissionElement::LangAttributeChanged() {
-  UpdateText();
+  UpdateAppearance();
   HTMLElement::LangAttributeChanged();
 }
 
@@ -827,7 +830,7 @@
     }
 
     is_precise_location_ = true;
-    UpdateText();
+    UpdateAppearance();
   }
 
   HTMLElement::AttributeChanged(params);
diff --git a/third_party/blink/renderer/core/html/html_permission_element.h b/third_party/blink/renderer/core/html/html_permission_element.h
index a959bd3..d90f991 100644
--- a/third_party/blink/renderer/core/html/html_permission_element.h
+++ b/third_party/blink/renderer/core/html/html_permission_element.h
@@ -115,7 +115,9 @@
   void setType(const AtomicString& type);
   uint16_t GetTranslatedMessageID(uint16_t message_id,
                                   const AtomicString& language_string);
-  virtual void UpdateText();
+  virtual void UpdateAppearance();
+
+  virtual void UpdateIcon(mojom::blink::PermissionName);
 
   // Update permission statuses and appearance based on the current statuses.
   virtual void UpdatePermissionStatusAndAppearance();
@@ -134,6 +136,8 @@
 
   bool is_precise_location() const { return is_precise_location_; }
 
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner();
+
  private:
   // TODO(crbug.com/1315595): remove this friend class once migration
   // to blink_unittests_v2 completes.
@@ -417,8 +421,6 @@
     return is_registered_in_browser_process_;
   }
 
-  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner();
-
   // Checks whether clicking is enabled at the moment. Clicking is disabled if
   // either:
   // 1) |DisableClickingIndefinitely| has been called and |EnableClicking| has
diff --git a/third_party/blink/renderer/core/html/html_permission_icon_element.cc b/third_party/blink/renderer/core/html/html_permission_icon_element.cc
index fe6554a7..dc42556 100644
--- a/third_party/blink/renderer/core/html/html_permission_icon_element.cc
+++ b/third_party/blink/renderer/core/html/html_permission_icon_element.cc
@@ -4,7 +4,9 @@
 
 #include "third_party/blink/renderer/core/html/html_permission_icon_element.h"
 
+#include "base/functional/bind.h"
 #include "third_party/blink/public/resources/grit/blink_resources.h"
+#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/html/html_permission_element_utils.h"
 #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
@@ -28,6 +30,17 @@
 
 void HTMLPermissionIconElement::SetIcon(PermissionName permission_type,
                                         bool is_precise_location) {
+  // We need `PostTask` here because creating a new element (the svg instead of
+  // setInnerHtml) starts dispatching events and it hits a DCHECK.
+  GetDocument()
+      .GetTaskRunner(TaskType::kInternalDefault)
+      ->PostTask(FROM_HERE, BindOnce(&HTMLPermissionIconElement::SetIconImpl,
+                                     WrapWeakPersistent(this), permission_type,
+                                     is_precise_location));
+}
+
+void HTMLPermissionIconElement::SetIconImpl(PermissionName permission_type,
+                                            bool is_precise_location) {
   if (is_icon_set_) {
     return;
   }
diff --git a/third_party/blink/renderer/core/html/html_permission_icon_element.h b/third_party/blink/renderer/core/html/html_permission_icon_element.h
index db23272..3bd945ca 100644
--- a/third_party/blink/renderer/core/html/html_permission_icon_element.h
+++ b/third_party/blink/renderer/core/html/html_permission_icon_element.h
@@ -23,6 +23,8 @@
                bool is_precise_location);
 
  private:
+  void SetIconImpl(mojom::blink::PermissionName permission_type,
+                   bool is_precise_location);
   // blink::Element overrides.
   void AdjustStyle(ComputedStyleBuilder& builder) override;
 
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index aeae3a6..78ce417f 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -2305,10 +2305,12 @@
           }
         }
         if (default_audio_track) {
-          default_audio_track->setEnabled(true);
+          default_audio_track->setEnabled(true,
+                                          TrackBase::ChangeSource::kInitial);
         }
         if (default_video_track) {
-          default_video_track->setSelected(true);
+          default_video_track->setSelected(true,
+                                           TrackBase::ChangeSource::kInitial);
         }
       }
     }
@@ -3329,7 +3331,8 @@
   return *audio_tracks_;
 }
 
-void HTMLMediaElement::AudioTrackChanged(AudioTrack* track) {
+void HTMLMediaElement::AudioTrackChanged(AudioTrack* track,
+                                         TrackBase::ChangeSource source) {
   DVLOG(3) << "audioTrackChanged(" << *this
            << ") trackId= " << String(track->id())
            << " enabled=" << base::ToString(track->enabled())
@@ -3344,8 +3347,10 @@
   if (media_source_attachment_)
     media_source_attachment_->OnTrackChanged(media_source_tracer_, track);
 
-  if (!audio_tracks_timer_.IsActive())
+  if (source != TrackBase::ChangeSource::kDemuxer &&
+      !audio_tracks_timer_.IsActive()) {
     audio_tracks_timer_.StartOneShot(base::TimeDelta(), FROM_HERE);
+  }
 }
 
 void HTMLMediaElement::AudioTracksTimerFired(TimerBase*) {
@@ -3363,7 +3368,9 @@
   return *video_tracks_;
 }
 
-void HTMLMediaElement::SelectedVideoTrackChanged(VideoTrack* track) {
+void HTMLMediaElement::SelectedVideoTrackChanged(
+    VideoTrack* track,
+    TrackBase::ChangeSource source) {
   DVLOG(3) << "selectedVideoTrackChanged(" << *this << ") selectedTrackId="
            << (track->selected() ? String(track->id()) : "none");
 
@@ -3372,13 +3379,16 @@
 
   videoTracks().ScheduleChangeEvent();
 
-  if (media_source_attachment_)
+  if (media_source_attachment_) {
     media_source_attachment_->OnTrackChanged(media_source_tracer_, track);
+  }
 
-  if (track->selected()) {
-    web_media_player_->SelectedVideoTrackChanged(track->id());
-  } else {
-    web_media_player_->SelectedVideoTrackChanged(std::nullopt);
+  if (source != TrackBase::ChangeSource::kDemuxer) {
+    if (track->selected()) {
+      web_media_player_->SelectedVideoTrackChanged(track->id());
+    } else {
+      web_media_player_->SelectedVideoTrackChanged(std::nullopt);
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h
index 56c78e2..b73586bd 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.h
+++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -44,6 +44,7 @@
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_state_observer.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/html/media/media_controls.h"
+#include "third_party/blink/renderer/core/html/track/track_base.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
 #include "third_party/blink/renderer/core/speech/speech_synthesis_base.h"
@@ -283,10 +284,10 @@
   void TogglePlayState();
 
   AudioTrackList& audioTracks();
-  void AudioTrackChanged(AudioTrack*);
+  void AudioTrackChanged(AudioTrack*, TrackBase::ChangeSource);
 
   VideoTrackList& videoTracks();
-  void SelectedVideoTrackChanged(VideoTrack*);
+  void SelectedVideoTrackChanged(VideoTrack*, TrackBase::ChangeSource);
 
   TextTrack* addTextTrack(const V8TextTrackKind& kind,
                           const AtomicString& label,
diff --git a/third_party/blink/renderer/core/html/media/html_media_element_test.cc b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
index 94a3ff97..f6f8303 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element_test.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
@@ -1018,6 +1018,65 @@
   EXPECT_TRUE(Media()->videoTracks().AnonymousIndexedGetter(0)->selected());
 }
 
+TEST_P(HTMLMediaElementTest, TrackChangeSourceDemuxerIsIgnored) {
+  Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
+  test::RunPendingTasks();
+  SetReadyState(HTMLMediaElement::kHaveFutureData);
+
+  ASSERT_EQ(1u, Media()->audioTracks().length());
+  ASSERT_EQ(1u, Media()->videoTracks().length());
+  AudioTrack* audio_track = Media()->audioTracks().AnonymousIndexedGetter(0);
+  VideoTrack* video_track = Media()->videoTracks().AnonymousIndexedGetter(0);
+
+  // Tracks are enabled by default. Let's disable them to test enabling.
+  // Use kScript source to ensure the player is notified.
+  EXPECT_CALL(*MockMediaPlayer(),
+              EnabledAudioTracksChanged(testing::Eq(std::nullopt)));
+  audio_track->setEnabled(false);
+  test::RunPendingTasks();
+  testing::Mock::VerifyAndClearExpectations(MockMediaPlayer());
+
+  EXPECT_CALL(*MockMediaPlayer(),
+              SelectedVideoTrackChanged(testing::Eq(std::nullopt)));
+  video_track->setSelected(false);
+  testing::Mock::VerifyAndClearExpectations(MockMediaPlayer());
+
+  // Test AudioTrackChanged with kDemuxer.
+  // We expect that EnabledAudioTracksChanged is NOT called for demuxer.
+  EXPECT_CALL(*MockMediaPlayer(), EnabledAudioTracksChanged(_)).Times(0);
+  audio_track->setEnabled(true, TrackBase::ChangeSource::kDemuxer);
+  test::RunPendingTasks();
+  testing::Mock::VerifyAndClearExpectations(MockMediaPlayer());
+
+  // Test SelectedVideoTrackChanged with kDemuxer.
+  // We expect that SelectedVideoTrackChanged is NOT called for demuxer.
+  EXPECT_CALL(*MockMediaPlayer(), SelectedVideoTrackChanged(_)).Times(0);
+  video_track->setSelected(true, TrackBase::ChangeSource::kDemuxer);
+  testing::Mock::VerifyAndClearExpectations(MockMediaPlayer());
+
+  // Disable the tracks again using a non-notifying source before testing the
+  // script source.
+  audio_track->setEnabled(false, TrackBase::ChangeSource::kDemuxer);
+  video_track->setSelected(false, TrackBase::ChangeSource::kDemuxer);
+  test::RunPendingTasks();
+  testing::Mock::VerifyAndClearExpectations(MockMediaPlayer());
+
+  // Test AudioTrackChanged with kScript.
+  // We expect that EnabledAudioTracksChanged IS called for script.
+  EXPECT_CALL(*MockMediaPlayer(),
+              EnabledAudioTracksChanged(testing::Optional(audio_track->id())));
+  audio_track->setEnabled(true, TrackBase::ChangeSource::kScript);
+  test::RunPendingTasks();
+  testing::Mock::VerifyAndClearExpectations(MockMediaPlayer());
+
+  // Test SelectedVideoTrackChanged with kScript.
+  // We expect that SelectedVideoTrackChanged IS called for script.
+  EXPECT_CALL(*MockMediaPlayer(),
+              SelectedVideoTrackChanged(testing::Optional(video_track->id())));
+  video_track->setSelected(true, TrackBase::ChangeSource::kScript);
+  testing::Mock::VerifyAndClearExpectations(MockMediaPlayer());
+}
+
 // Ensure a visibility observer is created for lazy loading.
 TEST_P(HTMLMediaElementTest, VisibilityObserverCreatedForLazyLoad) {
   Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
diff --git a/third_party/blink/renderer/core/html/resources/html.css b/third_party/blink/renderer/core/html/resources/html.css
index a1ac028..1d3d683 100644
--- a/third_party/blink/renderer/core/html/resources/html.css
+++ b/third_party/blink/renderer/core/html/resources/html.css
@@ -118,9 +118,7 @@
     margin-inline-end: auto;
     border-style: inset;
     border-width: 1px;
-    @supports blink-feature(HrElementGray) {
-        color: gray;
-    }
+    color: gray;
 }
 
 map {
@@ -948,6 +946,7 @@
       field-sizing: -internal-auto-base(initial, content);
       border: -internal-auto-base(1px solid light-dark(#767676, #858585), 1px solid currentColor);
       box-sizing: -internal-auto-base(border-box, initial);
+      block-size: -internal-auto-base(initial, calc(max(24px, 1lh) * attr(size type(<integer>), 4)));
     }
     @supports not blink-feature(CustomizableSelectInPage) {
       border-radius: 2px;
diff --git a/third_party/blink/renderer/core/html/resources/permission.css b/third_party/blink/renderer/core/html/resources/permission.css
index 1608b45e..a7165cf 100644
--- a/third_party/blink/renderer/core/html/resources/permission.css
+++ b/third_party/blink/renderer/core/html/resources/permission.css
@@ -50,6 +50,7 @@
   white-space:nowrap;
   word-wrap: normal;
   word-spacing: inherit;
+  -webkit-text-security: none !important;
 }
 
 permission::-internal-permission-container, geolocation::-internal-permission-container {
diff --git a/third_party/blink/renderer/core/html/track/audio_track.cc b/third_party/blink/renderer/core/html/track/audio_track.cc
index 22a03314..2e7c646 100644
--- a/third_party/blink/renderer/core/html/track/audio_track.cc
+++ b/third_party/blink/renderer/core/html/track/audio_track.cc
@@ -26,14 +26,14 @@
   TrackBase::Trace(visitor);
 }
 
-void AudioTrack::setEnabled(bool enabled) {
+void AudioTrack::setEnabled(bool enabled, ChangeSource source) {
   if (enabled == enabled_)
     return;
 
   enabled_ = enabled;
 
   if (MediaElement())
-    MediaElement()->AudioTrackChanged(this);
+    MediaElement()->AudioTrackChanged(this, source);
 }
 
 const AtomicString& AudioTrack::AlternativeKeyword() {
diff --git a/third_party/blink/renderer/core/html/track/audio_track.h b/third_party/blink/renderer/core/html/track/audio_track.h
index 696b4cd..a15db66 100644
--- a/third_party/blink/renderer/core/html/track/audio_track.h
+++ b/third_party/blink/renderer/core/html/track/audio_track.h
@@ -26,7 +26,7 @@
   void Trace(Visitor*) const override;
 
   bool enabled() const { return enabled_; }
-  void setEnabled(bool);
+  void setEnabled(bool enabled, ChangeSource source = ChangeSource::kScript);
 
   // Set enabled to false without notifying the owner media element. Used when
   // an exclusive audio track is enabled, implicitly disabling this one.
diff --git a/third_party/blink/renderer/core/html/track/track_base.h b/third_party/blink/renderer/core/html/track/track_base.h
index 81c4af0c..492008f0 100644
--- a/third_party/blink/renderer/core/html/track/track_base.h
+++ b/third_party/blink/renderer/core/html/track/track_base.h
@@ -38,6 +38,22 @@
 
 class CORE_EXPORT TrackBase : public Supplementable<TrackBase> {
  public:
+  // Subclasses of TrackBase including AudioTrack and VideoTrack allow changing
+  // whether a track is enabled or selected. There are three sources where this
+  // change can come from:
+  //  - The user controls (currently behind the experimental web platform flag)
+  //  - JS (currently behind the experimental web platform flag)
+  //  - The media pipeline (HLS demuxer, etc).
+  //  - The initial player startup.
+  // In order to prevent the circular notification of track changes, the source
+  // of the change is recorded here.
+  enum class ChangeSource {
+    kInitial,
+    kScript,
+    kUser,
+    kDemuxer,
+  };
+
   virtual ~TrackBase();
 
   String id() const { return id_; }
diff --git a/third_party/blink/renderer/core/html/track/video_track.cc b/third_party/blink/renderer/core/html/track/video_track.cc
index 20e0684..335070a 100644
--- a/third_party/blink/renderer/core/html/track/video_track.cc
+++ b/third_party/blink/renderer/core/html/track/video_track.cc
@@ -24,14 +24,14 @@
   TrackBase::Trace(visitor);
 }
 
-void VideoTrack::setSelected(bool selected) {
+void VideoTrack::setSelected(bool selected, ChangeSource source) {
   if (selected == selected_)
     return;
 
   selected_ = selected;
 
   if (MediaElement())
-    MediaElement()->SelectedVideoTrackChanged(this);
+    MediaElement()->SelectedVideoTrackChanged(this, source);
 }
 
 const AtomicString& VideoTrack::AlternativeKeyword() {
diff --git a/third_party/blink/renderer/core/html/track/video_track.h b/third_party/blink/renderer/core/html/track/video_track.h
index 273fe19..4052fcb0 100644
--- a/third_party/blink/renderer/core/html/track/video_track.h
+++ b/third_party/blink/renderer/core/html/track/video_track.h
@@ -25,7 +25,7 @@
   void Trace(Visitor*) const override;
 
   bool selected() const { return selected_; }
-  void setSelected(bool);
+  void setSelected(bool selected, ChangeSource source = ChangeSource::kScript);
   const AtomicString& kind() const { return kind_; }
 
   // Set selected to false without notifying the owner media element. Used when
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc b/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc
index d03660fd..8b8d1c4f 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc
@@ -69,6 +69,10 @@
 
 class ImageBitmapTest : public testing::Test {
  protected:
+  ImageBitmapTest()
+      : scoped_memory_cache_(MakeGarbageCollected<MemoryCache>(
+            blink::scheduler::GetSingleThreadTaskRunnerForTesting())) {}
+
   void SetUp() override {
     sk_sp<SkSurface> surface =
         SkSurfaces::Raster(SkImageInfo::MakeN32Premul(10, 10));
@@ -80,11 +84,6 @@
     surface2->getCanvas()->clear(0xAAAAAAAA);
     image2_ = surface2->makeImageSnapshot();
 
-    // Save the global memory cache to restore it upon teardown.
-    global_memory_cache_ =
-        ReplaceMemoryCacheForTesting(MakeGarbageCollected<MemoryCache>(
-            blink::scheduler::GetSingleThreadTaskRunnerForTesting()));
-
     test_context_provider_ = viz::TestContextProvider::CreateRaster();
     InitializeSharedGpuContextRaster(test_context_provider_.get());
   }
@@ -96,7 +95,6 @@
     ThreadState::Current()->CollectAllGarbageForTesting(
         ThreadState::StackState::kNoHeapPointers);
 
-    ReplaceMemoryCacheForTesting(global_memory_cache_.Release());
     SharedGpuContext::Reset();
   }
 
@@ -104,7 +102,7 @@
   test::TaskEnvironment task_environment_;
   scoped_refptr<viz::TestContextProvider> test_context_provider_;
   sk_sp<SkImage> image_, image2_;
-  Persistent<MemoryCache> global_memory_cache_;
+  ScopedMemoryCacheForTesting scoped_memory_cache_;
 };
 
 TEST_F(ImageBitmapTest, ImageResourceConsistency) {
diff --git a/third_party/blink/renderer/core/inspector/console_message.h b/third_party/blink/renderer/core/inspector/console_message.h
index c7700fe6..e1afc487 100644
--- a/third_party/blink/renderer/core/inspector/console_message.h
+++ b/third_party/blink/renderer/core/inspector/console_message.h
@@ -23,6 +23,14 @@
 class WorkerThread;
 struct WebConsoleMessage;
 
+// Represents a message to be shown in the DevTools console.
+//
+// Please familiarize yourself with http://goo.gle/devtools-console-policy prior
+// to introducing new console messages, and make sure that you understand the
+// implications on the developer experience. A good console message should be
+// actionable and relevant to what the developer is currently doing. Using the
+// DevTools Console panel as a means to advertise best practices or Chromium
+// agendas has shown to be counterproductive.
 class CORE_EXPORT ConsoleMessage final
     : public GarbageCollected<ConsoleMessage> {
  public:
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
index 4c63012..2849d01 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
@@ -185,7 +185,7 @@
 Element* GetPseudoIdAndTag(Element* element,
                            PseudoElement*& pseudo_element,
                            PseudoId& element_pseudo_id,
-                           AtomicString& view_transition_name) {
+                           AtomicString& pseudo_argument) {
   auto* resolved_element = element;
   auto* try_pseudo = DynamicTo<PseudoElement>(element);
   bool is_transition =
@@ -212,7 +212,7 @@
       return nullptr;
 
     element_pseudo_id = pseudo_element->GetPseudoIdForStyling();
-    view_transition_name = pseudo_element->view_transition_name();
+    pseudo_argument = pseudo_element->GetPseudoArgument();
   }
   return resolved_element;
 }
@@ -1364,13 +1364,13 @@
 
   Element* animating_element = element;
   PseudoId element_pseudo_id = kPseudoIdNone;
-  AtomicString view_transition_name = g_null_atom;
+  AtomicString pseudo_argument = g_null_atom;
   PseudoElement* pseudo_element = nullptr;
   // If the requested element is a view transition pseudo-element, `element`
   // becomes the first non-pseudo parent element or shadow host element after
   // `GetPseudoIdAndTag` call below.
   element = GetPseudoIdAndTag(element, pseudo_element, element_pseudo_id,
-                              view_transition_name);
+                              pseudo_argument);
   if (!element) {
     return protocol::Response::ServerError("Pseudo element has no parent");
   }
@@ -1442,13 +1442,13 @@
   Element* animating_element = element;
 
   PseudoId element_pseudo_id = kPseudoIdNone;
-  AtomicString view_transition_name = g_null_atom;
+  AtomicString pseudo_argument = g_null_atom;
   // If the requested element is a view transition pseudo-element, `element`
   // becomes the first non-pseudo parent element or shadow host element after
   // `GetPseudoIdAndTag` call below.
   PseudoElement* pseudo_element = nullptr;
   element = GetPseudoIdAndTag(element, pseudo_element, element_pseudo_id,
-                              view_transition_name);
+                              pseudo_argument);
   if (!element)
     return protocol::Response::ServerError("Pseudo element has no parent");
 
@@ -1477,13 +1477,12 @@
 
   CheckPseudoHasCacheScope check_pseudo_has_cache_scope(
       &document, /*within_selector_checking=*/false);
-  InspectorStyleResolver resolver(element, element_pseudo_id,
-                                  view_transition_name);
+  InspectorStyleResolver resolver(element, element_pseudo_id, pseudo_argument);
 
   // Matched rules.
   *matched_css_rules = BuildArrayForMatchedRuleList(
       resolver.MatchedRules(), element, ghost_rules, element_pseudo_id,
-      view_transition_name);
+      pseudo_argument);
 
   // Inherited styles.
   *inherited_entries =
@@ -1493,7 +1492,7 @@
         protocol::CSS::InheritedStyleEntry::create()
             .setMatchedCSSRules(BuildArrayForMatchedRuleList(
                 match->matched_rules, element, ghost_rules, element_pseudo_id,
-                view_transition_name))
+                pseudo_argument))
             .build();
     if (match->element->style() && match->element->style()->length()) {
       InspectorStyleSheetForInlineStyle* style_sheet =
@@ -1501,7 +1500,7 @@
       if (style_sheet) {
         entry->setInlineStyle(style_sheet->BuildObjectForStyle(
             style_sheet->InlineStyle(), element, element_pseudo_id,
-            view_transition_name));
+            pseudo_argument));
       }
     }
     (*inherited_entries)->emplace_back(std::move(entry));
@@ -1544,12 +1543,10 @@
                     match->pseudo_id))
                 .setMatches(BuildArrayForMatchedRuleList(
                     match->matched_rules, element, ghost_rules,
-                    match->pseudo_id, match->view_transition_name))
+                    match->pseudo_id, match->pseudo_argument))
                 .build());
-    if (match->view_transition_name) {
-      (*pseudo_id_matches)
-          ->back()
-          ->setPseudoIdentifier(match->view_transition_name);
+    if (match->pseudo_argument) {
+      (*pseudo_id_matches)->back()->setPseudoIdentifier(match->pseudo_argument);
     }
   }
 
@@ -1568,9 +1565,9 @@
               .setMatches(BuildArrayForMatchedRuleList(
                   pseudo_match->matched_rules, element, ghost_rules))
               .build());
-      if (pseudo_match->view_transition_name) {
+      if (pseudo_match->pseudo_argument) {
         parent_pseudo_element_matches->back()->setPseudoIdentifier(
-            pseudo_match->view_transition_name);
+            pseudo_match->pseudo_argument);
       }
     }
 
@@ -2269,12 +2266,12 @@
   ExecutionContext* execution_context = element->GetExecutionContext();
 
   if (pseudo_type.has_value()) {
-    AtomicString view_transition_name = pseudo_identifier.has_value()
-                                            ? AtomicString(*pseudo_identifier)
-                                            : g_null_atom;
+    AtomicString pseudo_argument = pseudo_identifier.has_value()
+                                       ? AtomicString(*pseudo_identifier)
+                                       : g_null_atom;
     element = element->GetStyledPseudoElement(
         InspectorDOMAgent::ProtocolPseudoTypeToPseudoId(*pseudo_type),
-        view_transition_name);
+        pseudo_argument);
     if (!element) {
       return protocol::Response::ServerError(
           "Could not retrieve pseudo element.");
@@ -4260,10 +4257,10 @@
 HeapVector<Member<CSSStyleDeclaration>> InspectorCSSAgent::MatchingStyles(
     Element* element) {
   PseudoId pseudo_id = kPseudoIdNone;
-  AtomicString view_transition_name = g_null_atom;
+  AtomicString pseudo_argument = g_null_atom;
   PseudoElement* pseudo_element = nullptr;
-  element = GetPseudoIdAndTag(element, pseudo_element, pseudo_id,
-                              view_transition_name);
+  element =
+      GetPseudoIdAndTag(element, pseudo_element, pseudo_id, pseudo_argument);
   if (!element)
     return {};
 
@@ -4284,8 +4281,7 @@
 
   HeapVector<Member<CSSStyleRule>> rules =
       FilterDuplicateRules(style_resolver.PseudoCSSRulesForElement(
-          element, pseudo_id, view_transition_name,
-          StyleResolver::kAllCSSRules));
+          element, pseudo_id, pseudo_argument, StyleResolver::kAllCSSRules));
   HeapVector<Member<CSSStyleDeclaration>> styles;
   if (!pseudo_id && element->style())
     styles.push_back(element->style());
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent_test.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent_test.cc
index a8d03e94..1449337 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent_test.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent_test.cc
@@ -41,7 +41,7 @@
                                              ASSERT_NO_EXCEPTION);
     CHECK(e);
     InspectorStyleResolver resolver(e, kPseudoIdNone,
-                                    /*view_transition_name=*/g_null_atom);
+                                    /*pseudo_argument=*/g_null_atom);
 
     HeapHashMap<Member<const ScopedCSSName>, Member<CSSFunctionRule>>
         function_rules;
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
index c214f26..16d38bac 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
@@ -2231,8 +2231,9 @@
     if (element->IsPseudoElement()) {
       value->setPseudoType(
           ProtocolPseudoElementType(element->GetPseudoIdForStyling()));
-      if (auto tag = To<PseudoElement>(element)->view_transition_name())
+      if (auto tag = To<PseudoElement>(element)->GetPseudoArgument()) {
         value->setPseudoIdentifier(tag);
+      }
     } else {
       if (!element->ownerDocument()->xmlVersion().empty())
         value->setXmlVersion(element->ownerDocument()->xmlVersion());
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
index 07b1937..baa2621 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
@@ -573,7 +573,7 @@
       SetRare(nodes->getPseudoType(nullptr), index,
               InspectorDOMAgent::ProtocolPseudoElementType(
                   element->GetPseudoIdForStyling()));
-      if (auto tag = To<PseudoElement>(element)->view_transition_name()) {
+      if (auto tag = To<PseudoElement>(element)->GetPseudoArgument()) {
         SetRare(nodes->getPseudoIdentifier(nullptr), index, tag);
       }
     }
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index 94d2f34..dec2f4b4a 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -809,8 +809,7 @@
 
 static std::unique_ptr<url_pattern::SimpleUrlPatternMatcher>
 BuildURLPatternMatcher(const String& pattern) {
-  return url_pattern::SimpleUrlPatternMatcher::Create(pattern.Utf8(),
-                                                      GURL("https://*"))
+  return url_pattern::SimpleUrlPatternMatcher::Create(pattern.Utf8(), nullptr)
       .value_or(std::unique_ptr<url_pattern::SimpleUrlPatternMatcher>());
 }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_preload_agent.cc b/third_party/blink/renderer/core/inspector/inspector_preload_agent.cc
index e4c7030..83d3fd2a 100644
--- a/third_party/blink/renderer/core/inspector/inspector_preload_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_preload_agent.cc
@@ -96,8 +96,9 @@
       return protocol::Preload::SpeculationActionEnum::Prerender;
     case mojom::blink::SpeculationAction::kPrefetch:
       return protocol::Preload::SpeculationActionEnum::Prefetch;
-    case mojom::blink::SpeculationAction::kPrefetchWithSubresources:
     case mojom::blink::SpeculationAction::kPrerenderUntilScript:
+      return protocol::Preload::SpeculationActionEnum::PrerenderUntilScript;
+    case mojom::blink::SpeculationAction::kPrefetchWithSubresources:
       NOTREACHED();
   }
 }
@@ -250,15 +251,10 @@
               PreloadingAttemptKeyHashTraits>
       preloading_attempts;
   for (SpeculationCandidate* candidate : candidates) {
-    // We are explicitly not reporting candidates for kPrefetchWithSubresources
-    // and kPrerenderUntilScript to clients, they are currently only interested
-    // in kPrefetch, kPrerender.
-    // TODO(https://crbug.com/428500219): Report kPrerenderUntilScript to
-    // clients.
+    // We are explicitly not reporting candidates for kPrefetchWithSubresources,
+    // because it is supposed to be replaced by kPrerenderUntilScript soon.
     if (candidate->action() ==
-            mojom::blink::SpeculationAction::kPrefetchWithSubresources ||
-        candidate->action() ==
-            mojom::blink::SpeculationAction::kPrerenderUntilScript) {
+        mojom::blink::SpeculationAction::kPrefetchWithSubresources) {
       continue;
     }
     PreloadingAttemptKey key = {candidate->action(), candidate->url(),
diff --git a/third_party/blink/renderer/core/inspector/inspector_style_resolver.cc b/third_party/blink/renderer/core/inspector/inspector_style_resolver.cc
index 73a7a6d4..a926e14b 100644
--- a/third_party/blink/renderer/core/inspector/inspector_style_resolver.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_style_resolver.cc
@@ -25,7 +25,7 @@
 InspectorStyleResolver::InspectorStyleResolver(
     Element* element,
     PseudoId element_pseudo_id,
-    const AtomicString& view_transition_name)
+    const AtomicString& pseudo_argument)
     : element_(element) {
   DCHECK(element_);
 
@@ -40,7 +40,7 @@
   StyleResolver& style_resolver = element_->GetDocument().GetStyleResolver();
 
   matched_rules_ = style_resolver.PseudoCSSRulesForElement(
-      element_, element_pseudo_id, view_transition_name,
+      element_, element_pseudo_id, pseudo_argument,
       StyleResolver::kAllCSSRules);
 
   // At this point, the pseudo-element id for scroll marker groups has been
@@ -149,23 +149,23 @@
 
 void InspectorStyleResolver::AddPseudoElementRules(
     PseudoId pseudo_id,
-    const AtomicString& view_transition_name) {
+    const AtomicString& pseudo_argument) {
   StyleResolver& style_resolver = element_->GetDocument().GetStyleResolver();
   // If the pseudo-element doesn't exist, exclude UA rules to avoid cluttering
   // all elements.
   unsigned rules_to_include =
-      element_->GetStyledPseudoElement(pseudo_id, view_transition_name)
+      element_->GetStyledPseudoElement(pseudo_id, pseudo_argument)
           ? StyleResolver::kAllCSSRules
           : StyleResolver::kAllButUACSSRules;
   RuleIndexList* matched_rules = style_resolver.PseudoCSSRulesForElement(
-      element_, pseudo_id, view_transition_name, rules_to_include);
+      element_, pseudo_id, pseudo_argument, rules_to_include);
   if (matched_rules && matched_rules->size()) {
     InspectorCSSMatchedRules* match =
         MakeGarbageCollected<InspectorCSSMatchedRules>();
     match->element = element_;
     match->matched_rules = matched_rules;
     match->pseudo_id = pseudo_id;
-    match->view_transition_name = view_transition_name;
+    match->pseudo_argument = pseudo_argument;
     pseudo_element_rules_.push_back(match);
   }
 }
diff --git a/third_party/blink/renderer/core/inspector/inspector_style_resolver.h b/third_party/blink/renderer/core/inspector/inspector_style_resolver.h
index c46c184..e4fcc2f 100644
--- a/third_party/blink/renderer/core/inspector/inspector_style_resolver.h
+++ b/third_party/blink/renderer/core/inspector/inspector_style_resolver.h
@@ -26,7 +26,7 @@
   Member<Element> element;
   Member<RuleIndexList> matched_rules;
   PseudoId pseudo_id;
-  AtomicString view_transition_name = g_null_atom;
+  AtomicString pseudo_argument = g_null_atom;
 
   void Trace(Visitor* visitor) const {
     visitor->Trace(element);
@@ -54,7 +54,7 @@
  public:
   InspectorStyleResolver(Element*,
                          PseudoId,
-                         const AtomicString& view_transition_name);
+                         const AtomicString& pseudo_argument);
   RuleIndexList* MatchedRules() const;
   HeapVector<Member<InspectorCSSMatchedRules>> PseudoElementRules();
   HeapVector<Member<InspectorCSSMatchedRules>> ParentRules();
@@ -63,7 +63,7 @@
 
  private:
   void AddPseudoElementRules(PseudoId pseudo_id,
-                             const AtomicString& view_transition_name);
+                             const AtomicString& pseudo_argument);
 
   Element* element_;
   RuleIndexList* matched_rules_;
diff --git a/third_party/blink/renderer/core/layout/column_layout_algorithm.cc b/third_party/blink/renderer/core/layout/column_layout_algorithm.cc
index 28f789e..7086187 100644
--- a/third_party/blink/renderer/core/layout/column_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/column_layout_algorithm.cc
@@ -12,7 +12,6 @@
 #include "third_party/blink/renderer/core/layout/column_spanner_path.h"
 #include "third_party/blink/renderer/core/layout/constraint_space_builder.h"
 #include "third_party/blink/renderer/core/layout/fragmentation_utils.h"
-#include "third_party/blink/renderer/core/layout/gap/gap_geometry.h"
 #include "third_party/blink/renderer/core/layout/geometry/fragment_geometry.h"
 #include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
 #include "third_party/blink/renderer/core/layout/geometry/margin_strut.h"
@@ -354,60 +353,49 @@
   container_builder_.HandleOofsAndSpecialDescendants();
 
   if (RuntimeEnabledFeatures::CSSGapDecorationEnabled() &&
-      Style().HasGapRule()) {
-    bool has_column_gaps = !cross_gaps_.empty();
-    bool has_row_gaps = !main_gaps_.empty();
-    if (has_column_gaps || has_row_gaps) {
-      GapGeometry* gap_geometry =
-          MakeGarbageCollected<GapGeometry>(GapGeometry::kMultiColumn);
-        LayoutUnit applicable_border_scrollbar_padding_block_end =
-            container_builder_.ApplicableBorders().block_end +
-            container_builder_.ApplicableScrollbar().block_end +
-            container_builder_.ApplicablePadding().block_end;
-        LayoutUnit fragment_block_size = container_builder_.FragmentBlockSize();
+      Style().HasGapRule() && (!cross_gaps_.empty() || !main_gaps_.empty())) {
+    auto* gap_geometry =
+        MakeGarbageCollected<GapGeometry>(GapGeometry::kMultiColumn);
 
-        // For the content inline and block ends, we must take the max of where
-        // the fragment starts and ends and where the last cross gap and main
-        // gap are. This is so that when content overflows the container, we
-        // still paint the gap decorations.
-        LayoutUnit content_inline_end =
-            !cross_gaps_.empty()
-                ? std::max(cross_gaps_.back().GetGapOffset().inline_offset,
-                           container_builder_.FragmentInlineSize() -
-                               BorderScrollbarPadding().inline_end)
-                : container_builder_.FragmentInlineSize() -
-                      BorderScrollbarPadding().inline_end;
-        LayoutUnit content_block_end =
-            !main_gaps_.empty()
-                ? std::max(main_gaps_.back().GetGapOffset(),
-                           fragment_block_size -
-                               applicable_border_scrollbar_padding_block_end)
-                : fragment_block_size -
-                      applicable_border_scrollbar_padding_block_end;
-        // TODO(crbug.com/440123087): Risky since they could in theory be used
-        // after moved. Clean up to not move members. Change members to
-        // unique_ptrs.
-        if (has_column_gaps) {
-          gap_geometry->SetCrossGaps(std::move(cross_gaps_));
-          gap_geometry->SetInlineGapSize(column_gap_size_);
-        }
-        if (has_row_gaps) {
-          gap_geometry->SetMainGaps(std::move(main_gaps_));
-          gap_geometry->SetBlockGapSize(row_gap_size_);
-        }
-
-        CHECK(content_inline_start_.has_value());
-        CHECK(content_block_start_.has_value());
-        gap_geometry->SetContentInlineOffsets(*content_inline_start_,
-                                              content_inline_end);
-        gap_geometry->SetContentBlockOffsets(*content_block_start_,
-                                             content_block_end);
-
-        // For multicol, the main direction will always be the rows.
-        gap_geometry->SetMainDirection(kForRows);
-
-      container_builder_.SetGapGeometry(gap_geometry);
+    // For the content inline and block ends, we must take the max of where the
+    // fragment starts and ends and where the last cross gap and main gap are.
+    // This is so that when content overflows the container, we still paint the
+    // gap decorations.
+    LayoutUnit content_inline_end = container_builder_.FragmentInlineSize() -
+                                    BorderScrollbarPadding().inline_end;
+    if (!cross_gaps_.empty()) {
+      content_inline_end = std::max(
+          content_inline_end, cross_gaps_.back().GetGapOffset().inline_offset);
+      gap_geometry->SetCrossGaps(std::move(cross_gaps_));
+      gap_geometry->SetInlineGapSize(column_gap_size_);
     }
+
+    LayoutUnit content_block_end =
+        container_builder_.FragmentBlockSize() -
+        container_builder_.ApplicableBorders().block_end -
+        container_builder_.ApplicableScrollbar().block_end -
+        container_builder_.ApplicablePadding().block_end;
+    if (!main_gaps_.empty()) {
+      // TODO(crbug.com/357648037): There is content beyond the last main gap,
+      // so using this as the offset isn't right. The bug here is that if the
+      // multicol container is overflowed, the column gaps in the last row will
+      // be missing.
+      content_block_end =
+          std::max(content_block_end, main_gaps_.back().GetGapOffset());
+      gap_geometry->SetMainGaps(std::move(main_gaps_));
+      gap_geometry->SetBlockGapSize(row_gap_size_);
+    }
+
+    CHECK(first_column_offset_.has_value());
+    gap_geometry->SetContentInlineOffsets(first_column_offset_->inline_offset,
+                                          content_inline_end);
+    gap_geometry->SetContentBlockOffsets(first_column_offset_->block_offset,
+                                         content_block_end);
+
+    // For multicol, the main direction will always be the rows.
+    gap_geometry->SetMainDirection(kForRows);
+
+    container_builder_.SetGapGeometry(gap_geometry);
   }
 
   return container_builder_.ToBoxFragment();
@@ -672,6 +660,18 @@
     // row-gap.
     LayoutUnit line_offset = intrinsic_block_size_ + margin_strut->Sum();
 
+    if (RuntimeEnabledFeatures::CSSGapDecorationEnabled() && is_first_row &&
+        Style().HasGapRule() && !main_gaps_.empty() &&
+        main_gaps_.back().IsStartSpannerMainGap()) {
+      // We are preceded by one or more spanners. Carve another mark, denoting
+      // the end of the column rules break that started at the first (or only)
+      // spanner, so that column rules may resume from now on.
+      main_gaps_.emplace_back(line_offset, SpannerMainGapType::kEnd);
+
+      // There should be no column gaps here, since we just dealt with spanners.
+      DCHECK(!first_trailing_column_gap_idx_);
+    }
+
     if (!is_first_row) {
       line_offset += row_gap_size_;
     }
@@ -696,89 +696,6 @@
   return result;
 }
 
-void ColumnLayoutAlgorithm::AddCrossGapForColumn(LayoutUnit inline_offset,
-                                                 LayoutUnit block_offset) {
-  CrossGap::EdgeIntersectionState state =
-      CrossGap::EdgeIntersectionState::kBoth;
-
-  cross_gaps_.emplace_back(LogicalOffset(inline_offset, block_offset), state);
-
-  // We increment the range of cross gaps associated with the current gap
-  // whenever we add a new cross gap. This range is then "flushed"/"committed"
-  // to the main gap each time we process a row.
-  range_of_cross_gaps_before_current_main_gap_.Increment(cross_gaps_.size() -
-                                                         1);
-}
-
-void ColumnLayoutAlgorithm::AddMainGapForSpanner(
-    LayoutUnit block_offset,
-    LayoutUnit logical_fragment_block_size) {
-  // The only situation where we would expect the range to not be valid is if
-  // there are spanners placed back-to-back with no content in between. As far
-  // as GapDecorations is related, in these cases we "condense" the sequence
-  // of consecutive spanners into a single spanner, by having only two
-  // MainGaps (a start and an end) represent the entire sequence of spanners.
-  bool spanner_placed_after_spanner_with_no_content_in_between =
-      !range_of_cross_gaps_before_current_main_gap_.IsValid();
-
-  // If we have consecutive spanners with no content in between, we remove the
-  // last end spanner main gap, and the new spanner's end will become the end
-  // of the big condensed spanner.
-  if (spanner_placed_after_spanner_with_no_content_in_between) {
-    main_gaps_.pop_back();
-  } else {
-    // As described in third_party/blink/renderer/core/layout/gap/README.md,
-    // we create a `MainGap` at the spanner's block start offset when we have
-    // a spanner, and another `MainGap` at the spanner's block end offset.
-    main_gaps_.emplace_back(block_offset, SpannerMainGapType::kStart);
-  }
-
-  // A spanner ends the current range of cross gaps, since it leads to the
-  // introduction of new `CrossGap`s.
-  CommitRangeOfCrossGapsBeforeCurrentMainGap();
-
-  main_gaps_.emplace_back(block_offset + logical_fragment_block_size,
-                          SpannerMainGapType::kEnd);
-
-  CHECK_GE(main_gaps_.size(), 2u);
-
-  // Even though this last `MainGap` is a spanner end gap, we set its cross
-  // gap before range to be that of the main gap before this one.
-  main_gaps_.back().SetRangeOfCrossGapsBefore(
-      main_gaps_[main_gaps_.size() - 2].RangeOfCrossGapsBefore());
-  CHECK(main_gaps_.back().RangeOfCrossGapsBefore().IsValid());
-}
-
-void ColumnLayoutAlgorithm::ResetRangeOfCrossGapsBeforeCurrentMainGap() {
-  range_of_cross_gaps_before_current_main_gap_ = CrossGapRange();
-}
-
-void ColumnLayoutAlgorithm::CommitRangeOfCrossGapsBeforeCurrentMainGap() {
-  if (main_gaps_.empty() || cross_gaps_.empty()) {
-    return;
-  }
-
-  CHECK(!main_gaps_.back().IsEndSpannerMainGap());
-
-  if (!range_of_cross_gaps_before_current_main_gap_.IsValid()) {
-    // The only situation where we would expect the range to not be valid is if
-    // we have inserted multiple spanners in a row (back to back, with no space
-    // in between, each spanner accounting for 2 `MainGap`s). In such cases, we
-    // need to use the range from the previously inserted main gap, which will
-    // be valid. Subsequent calls to this after each spanner in the sequence
-    // will always then find that same valid range.
-    CHECK_GE(main_gaps_.size(), 1u);
-    range_of_cross_gaps_before_current_main_gap_ =
-        main_gaps_[main_gaps_.size() - 1].RangeOfCrossGapsBefore();
-    CHECK(range_of_cross_gaps_before_current_main_gap_.IsValid());
-  }
-
-  main_gaps_.back().SetRangeOfCrossGapsBefore(
-      range_of_cross_gaps_before_current_main_gap_);
-
-  ResetRangeOfCrossGapsBeforeCurrentMainGap();
-}
-
 struct ResultWithOffset {
   DISALLOW_NEW();
 
@@ -1252,10 +1169,14 @@
     num_columns = next_column_token->SequenceNumber() + 1;
   }
 
-
-      if (RuntimeEnabledFeatures::CSSGapDecorationEnabled()) {
-        cross_gaps_.reserve(cross_gaps_.size() + new_columns.size() - 1);
-      }
+  if (RuntimeEnabledFeatures::CSSGapDecorationEnabled() && has_wrapped &&
+      Style().HasGapRule() && row_gap_size_ > LayoutUnit()) {
+    // This is right after a column wrap. Since we're here, we're finally
+    // positive that another line of columns is created. Add the preceding row
+    // gap to allow for a row rule before this new row, and also so that column
+    // rules belonging to the previous row are properly terminated.
+    AddMainGap(line_offset - row_gap_size_);
+  }
 
   wtf_size_t column_index_in_row = 0;
 
@@ -1284,35 +1205,23 @@
 
     if (RuntimeEnabledFeatures::CSSGapDecorationEnabled() &&
         Style().HasGapRule()) {
-      // As described in third_party/blink/renderer/core/layout/gap/README.md,
-      // we create a `MainGap` when we have a row gap.
-      if (column_index_in_row == 0 && has_wrapped &&
-          row_gap_size_ > LayoutUnit()) {
-        // If this `MainGap` is not for a spanner, the offset will be the
-        // start of the row gap. Otherwise, it will
-        // be the start of the spanner.
-        main_gaps_.emplace_back(column_logical_rect.BlockStartOffset() -
-                                row_gap_size_);
-        CommitRangeOfCrossGapsBeforeCurrentMainGap();
-      }
-
       // The first column in a row has no associated column intersections.
       if (column_index_in_row > 0) {
-        // We add a cross gap at the start of for every column in a
-        // row, after the first column, since there is no gap before the first
-        // column.
-        //
-        // See third_party/blink/renderer/core/layout/gap/README.md for more
-        // information.
-        AddCrossGapForColumn(
-            column_logical_rect.InlineStartOffset() - (column_gap_size_ / 2),
-            column_logical_rect.BlockStartOffset());
+        if (!first_trailing_column_gap_idx_) {
+          // When there's a subsequent main gap (row gap or before a column
+          // spanner), this will be the first column gap to be affected by that.
+          first_trailing_column_gap_idx_ = cross_gaps_.size();
+        }
+        LayoutUnit gap_center =
+            column_logical_rect.InlineStartOffset() - (column_gap_size_ / 2);
+        cross_gaps_.emplace_back(
+            LogicalOffset(gap_center, column_logical_rect.BlockStartOffset()),
+            CrossGap::EdgeIntersectionState::kBoth);
       }
 
-      if (!content_inline_start_.has_value() &&
-          !content_block_start_.has_value()) {
-        content_inline_start_ = column_logical_rect.InlineStartOffset();
-        content_block_start_ = column_logical_rect.BlockStartOffset();
+      if (!first_column_offset_.has_value()) {
+        first_column_offset_.emplace(column_logical_rect.InlineStartOffset(),
+                                     column_logical_rect.BlockStartOffset());
       }
     }
 
@@ -1400,19 +1309,35 @@
   *margin_strut = MarginStrut();
   margin_strut->Append(margins.block_end, /* is_quirky */ false);
 
+  if (RuntimeEnabledFeatures::CSSGapDecorationEnabled() &&
+      Style().HasGapRule() && !cross_gaps_.empty()) {
+    if (main_gaps_.empty() || !main_gaps_.back().IsStartSpannerMainGap()) {
+      // This spanner is preceded by column content (because there are cross
+      // gaps, and no preceding adjacent spanner). Insert a break for column
+      // rules. They are not to overlap with the margin box of spanners.
+      AddMainGap(intrinsic_block_size_, SpannerMainGapType::kStart);
+    }
+  }
+
   intrinsic_block_size_ = offset.block_offset + logical_fragment.BlockSize();
   has_processed_first_child_ = true;
 
-  // If the spanner comes before the start of any cross gaps, we skip it since
-  // it won't affect the cross gaps.
-  if (RuntimeEnabledFeatures::CSSGapDecorationEnabled() &&
-      Style().HasGapRule() && !cross_gaps_.empty()) {
-    AddMainGapForSpanner(offset.block_offset, logical_fragment.BlockSize());
-  }
-
   return BreakStatus::kContinue;
 }
 
+void ColumnLayoutAlgorithm::AddMainGap(LayoutUnit block_offset,
+                                       SpannerMainGapType gap_type) {
+  main_gaps_.emplace_back(block_offset, gap_type);
+
+  // Terminate preceding adjacent column gaps.
+  if (!first_trailing_column_gap_idx_) {
+    return;
+  }
+  CrossGapRange range(*first_trailing_column_gap_idx_, cross_gaps_.size() - 1);
+  main_gaps_.back().SetRangeOfCrossGapsBefore(range);
+  first_trailing_column_gap_idx_.reset();
+}
+
 void ColumnLayoutAlgorithm::AttemptToPositionListMarker(
     const PhysicalBoxFragment& child_fragment,
     LayoutUnit block_offset) {
diff --git a/third_party/blink/renderer/core/layout/column_layout_algorithm.h b/third_party/blink/renderer/core/layout/column_layout_algorithm.h
index 015f622..874550d0 100644
--- a/third_party/blink/renderer/core/layout/column_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/column_layout_algorithm.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/box_fragment_builder.h"
+#include "third_party/blink/renderer/core/layout/gap/gap_geometry.h"
 #include "third_party/blink/renderer/core/layout/layout_algorithm.h"
 
 namespace blink {
@@ -19,6 +20,57 @@
 struct LogicalSize;
 struct MarginStrut;
 
+// Multicol layout algorithm.
+//
+// Establishes a fragmentation context and produces columns as child fragments.
+// Each column is a fragmentainer that contains a portion of the fragmented
+// content.
+//
+// Additionally column spanners are also added as child fragments, if they
+// exist. They are taken out of the fragmentation context and become siblings of
+// column fragments.
+//
+// The multicol spec has the concept of "lines" and "rows". A multicol container
+// contains rows, which contain lines and/or spanners. Lines contain columns.
+// This algorithm obviously needs to be aware of these concepts, but no
+// fragments are created for rows or lines. It's just plain columns that are
+// positioned relatively to the resulting multicol fragment.
+//
+// Gap decorations:
+//
+// This algorithm also sets up gap decorations, i.e. column rules and row rules.
+// Example of a multicol container with a spanner, and a row gap.
+// Gap intersections are given by `X`.
+// +------------------------X-----------------------------------------+
+// | +---------+           Column Gap     +---------+                 |
+// | |         |                          |         |                 |
+// | +---------+                          +---------+                 |
+// |                                                                  |
+// |------------------------X-----------------------------------------|
+// |                    Spanner                                       |
+// |------------------------X-----------------------------------------|
+// |                                                                  |
+// | +---------+           Column Gap     +---------+                 |
+// | |         |                          |         |                 |
+// | +---------+                          +---------+                 |
+// X=========Row Gap========X=========================================X
+// |                                                                  |
+// | +---------+           Column Gap                                 |
+// | |         |                                                      |
+// | +---------+                                                      |
+// +------------------------X-----------------------------------------+
+//
+// To populate the gap intersections, we build them out as we place each column
+// in a row of columns.
+//
+// Each column in a line of columns, except for the first column, can be
+// associated with the following gap intersections:
+// * The column intersection of the column gap with the first or last edge of
+//   the container (in the block direction).
+// * The column intersection of the column gap with any spanner before the
+//   column.
+//
+// See third_party/blink/renderer/core/layout/gap/README.md for more info.
 class CORE_EXPORT ColumnLayoutAlgorithm
     : public LayoutAlgorithm<BlockNode, BoxFragmentBuilder, BlockBreakToken> {
  public:
@@ -74,74 +126,10 @@
                             const BlockBreakToken* break_token,
                             MarginStrut*);
 
-  // GapDecorations:
-  // Example of a multicol container with a spanner, and a row gap.
-  // Gap intersections are given by `X`.
-  // +------------------------X-----------------------------------------+
-  // | +---------+           Column Gap     +---------+                 |
-  // | |         |                          |         |                 |
-  // | +---------+                          +---------+                 |
-  // |                                                                  |
-  // |------------------------X-----------------------------------------|
-  // |                    Spanner                                       |
-  // |------------------------X-----------------------------------------|
-  // |                                                                  |
-  // | +---------+           Column Gap     +---------+                 |
-  // | |         |                          |         |                 |
-  // | +---------+                          +---------+                 |
-  // X=========Row Gap========X=========================================X
-  // |                                                                  |
-  // | +---------+           Column Gap                                 |
-  // | |         |                                                      |
-  // | +---------+                                                      |
-  // +------------------------X-----------------------------------------+
-  // To populate the gap intersections, we build them out as we place each
-  // column in a row of columns. If we run into a spanner, we modify the column
-  // intersections above the spanner to be "blocked after". If we run into a
-  // row gap, we build its intersections and modify the column intersections
-  // right above the row gap so that they fall in the middle of the row gap.
-  //
-  // Each column in a row of columns, except for the first column, can be
-  // associated with the following gap intersections:
-  // * The column intersection of the column gap with the first or last edge of
-  // the container (in the block direction).
-  // * The column intersection of the column gap with any spanner before the
-  // column.
-  void BuildGapIntersectionsForColumn(wtf_size_t column_index_in_row,
-                                      const LogicalRect& column_logical_rect,
-                                      bool has_wrapped,
-                                      bool row_preceeds_spanner);
-
-  // If a row gap exists, this will build the gap intersections for that row
-  // gap. These include:
-  // * Row intersections at the start and end of the row.
-  // * Row intersections of that row gap with any column gaps.
-  // * Column intersections of the row gap with any column gaps.
-  //
-  // We only need to do this once per row of columns.
-  void AdjustEveryColumnLastGapIntersectionsWithRowGap(LayoutUnit row_offset);
-
-  // If we have a row gap, we need to build the intersections of that row gap
-  // with each column gap separately. We need to do this once per row of
-  // columns, since it could be the case that the last row of columns has fewer
-  // columns than the row before it.
-  void BuildRowGapIntersections(const LogicalRect& column_logical_rect,
-                                GapIntersectionList& row_gap_intersections);
-
-  // TODO(crbug.com/436140061): The following are for the optimized version of
-  // GapDecorations. Once the optimized version is implemented, we can remove
-  // all the other unused methods and members from the old version.
-
-  // Gap decorations:
-  // * `CrossGap`s are the column gaps. The presence of a spanner will create a
-  // new `CrossGap` for each column gap.
-  // * `MainGap`s are the row gaps created by `column-wrap: wrap`. We will
-  // also have a `MainGap` for each spanner.
-  // See third_party/blink/renderer/core/layout/gap/README.md for more info.
-  void AddCrossGapForColumn(LayoutUnit inline_offset, LayoutUnit block_offset);
-
-  void AddMainGapForSpanner(LayoutUnit block_offset,
-                            LayoutUnit logical_fragment_block_size);
+  // Add another main gap, at the given offset. This is either the block-start
+  // of a row gap, or before or after a spanner.
+  void AddMainGap(LayoutUnit block_offset,
+                  SpannerMainGapType gap_type = SpannerMainGapType::kNone);
 
   // Populates `range_of_cross_gaps_before_current_main_gap_` with
   // `CrossGapRanges` for each group of `CrossGap`s before each `MainGap`.
@@ -150,7 +138,6 @@
   // needed by Paint to calculate the intersection points of row gaps and column
   // gaps.
   void CommitRangeOfCrossGapsBeforeCurrentMainGap();
-  void ResetRangeOfCrossGapsBeforeCurrentMainGap();
 
   // Attempt to position the list-item marker (if any) beside the child
   // fragment. This requires the fragment to have a baseline. If it doesn't,
@@ -248,13 +235,23 @@
   LayoutUnit column_gap_size_;
   LayoutUnit row_gap_size_;
 
-  //`main_gaps_` are the row gaps, while `cross_gaps_` are the column gaps.
+  // One entry for each row gap, and one entry between column content and
+  // spanners. There is no gap between column content and spanners, but column
+  // gaps need to be interrupted, since they shouldn't necessarily overlap with
+  // spanners.
   Vector<MainGap> main_gaps_;
-  Vector<CrossGap> cross_gaps_;
-  CrossGapRange range_of_cross_gaps_before_current_main_gap_;
 
-  std::optional<LayoutUnit> content_inline_start_;
-  std::optional<LayoutUnit> content_block_start_;
+  // One entry for each column gap.
+  Vector<CrossGap> cross_gaps_;
+
+  // Index of the first column gap that gets terminated by any subsequent main
+  // gap (row gap or spanner).
+  std::optional<wtf_size_t> first_trailing_column_gap_idx_;
+
+  // Offset to the first column (in the first row), from the start border edge
+  // of the resulting multicol fragment. Will only be set if needed, i.e. for
+  // gap decorations.
+  std::optional<LogicalOffset> first_column_offset_;
 
   // This will be set during (outer) block fragmentation once we've processed
   // the first piece of content of the multicol container. It is used to check
diff --git a/third_party/blink/renderer/core/layout/gap/gap_geometry.cc b/third_party/blink/renderer/core/layout/gap/gap_geometry.cc
index c3e11c86..6e09f91 100644
--- a/third_party/blink/renderer/core/layout/gap/gap_geometry.cc
+++ b/third_party/blink/renderer/core/layout/gap/gap_geometry.cc
@@ -375,9 +375,14 @@
         // The intersection at an end spanner main gap must still be added to
         // the vector, so we can paint behind spanners with `rule-break: none`.
         wtf_size_t spanner_index = main_gap_running_index_ + 1;
-        CHECK(main_gaps_[spanner_index].IsEndSpannerMainGap());
-        CHECK_LT(spanner_index, main_gaps_.size());
-        intersections.push_back(main_gaps_[spanner_index].GetGapOffset());
+        if (spanner_index < main_gaps_.size()) {
+          CHECK(main_gaps_[spanner_index].IsEndSpannerMainGap());
+          intersections.push_back(main_gaps_[spanner_index].GetGapOffset());
+        } else {
+          // If there is no column content after a spanner, there'll be no
+          // EndSpannerMainGap.
+          intersections.push_back(content_block_end_);
+        }
       }
       break;
   }
diff --git a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
index d794303..3cc16c72a 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
@@ -2300,7 +2300,8 @@
   // decorations).
   if ((RuntimeEnabledFeatures::CSSGapDecorationEnabled() &&
        Style().HasGapRule()) ||
-      out_unfragmented_gap_geometry) {
+      (RuntimeEnabledFeatures::CSSGridGapSuppressionEnabled() &&
+       out_unfragmented_gap_geometry)) {
     gap_accumulator = GapAccumulator();
     gap_accumulator->BuildGapGeometry(layout_data);
 
@@ -2422,7 +2423,8 @@
       // If `out_unfragmented_gap_geometry` is present we just want to record
       // the initial position of all gaps for the purposes of fragmentation.
       // Don't add these to the builder.
-      if (out_unfragmented_gap_geometry) {
+      if (RuntimeEnabledFeatures::CSSGridGapSuppressionEnabled() &&
+          out_unfragmented_gap_geometry) {
         *out_unfragmented_gap_geometry = gap_geometry;
       } else {
         container_builder_.SetGapGeometry(gap_geometry);
@@ -3020,7 +3022,9 @@
                  max_item_block_end - cloned_block_start_decoration);
   }
 
-  PlaceGaps();
+  if (RuntimeEnabledFeatures::CSSGridGapSuppressionEnabled()) {
+    PlaceGaps();
+  }
 
   if (has_subsequent_children)
     container_builder_.SetHasSubsequentChildren();
diff --git a/third_party/blink/renderer/core/layout/layout_text.cc b/third_party/blink/renderer/core/layout/layout_text.cc
index 06876fe..70b7c1b 100644
--- a/third_party/blink/renderer/core/layout/layout_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_text.cc
@@ -626,9 +626,7 @@
         // ​​are equal, it signifies a collapsed range. In this case, we
         // should skip processing `item`.
         if (start > offset.end || end < offset.start ||
-            (RuntimeEnabledFeatures::
-                 SkipLineBreakItemWhenIsCollapsedEnabled() &&
-             item.IsLineBreak() && start == end)) {
+            (item.IsLineBreak() && start == end)) {
           is_last_end_included = false;
           continue;
         }
diff --git a/third_party/blink/renderer/core/loader/resource/css_style_sheet_resource_test.cc b/third_party/blink/renderer/core/loader/resource/css_style_sheet_resource_test.cc
index da28085..38deece 100644
--- a/third_party/blink/renderer/core/loader/resource/css_style_sheet_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/css_style_sheet_resource_test.cc
@@ -37,15 +37,9 @@
 
 class CSSStyleSheetResourceTest : public PageTestBase {
  protected:
-  CSSStyleSheetResourceTest() {
-    original_memory_cache_ =
-        ReplaceMemoryCacheForTesting(MakeGarbageCollected<MemoryCache>(
-            blink::scheduler::GetSingleThreadTaskRunnerForTesting()));
-  }
-
-  ~CSSStyleSheetResourceTest() override {
-    ReplaceMemoryCacheForTesting(original_memory_cache_.Release());
-  }
+  CSSStyleSheetResourceTest()
+      : scoped_memory_cache_(MakeGarbageCollected<MemoryCache>(
+            blink::scheduler::GetSingleThreadTaskRunnerForTesting())) {}
 
   void SetUp() override {
     PageTestBase::SetUp(gfx::Size());
@@ -66,7 +60,7 @@
     return css_resource;
   }
 
-  Persistent<MemoryCache> original_memory_cache_;
+  ScopedMemoryCacheForTesting scoped_memory_cache_;
 };
 
 TEST_F(CSSStyleSheetResourceTest, DuplicateResourceNotCached) {
diff --git a/third_party/blink/renderer/core/loader/resource/svg_document_resource.cc b/third_party/blink/renderer/core/loader/resource/svg_document_resource.cc
index a84515c3..f3a3967 100644
--- a/third_party/blink/renderer/core/loader/resource/svg_document_resource.cc
+++ b/third_party/blink/renderer/core/loader/resource/svg_document_resource.cc
@@ -71,6 +71,18 @@
                    decoder_options),
       content_(content) {}
 
+bool SVGDocumentResource::CanUseCacheValidator() const {
+  if (RuntimeEnabledFeatures::
+          SvgPartitionSVGDocumentResourcesInMemoryCacheEnabled()) {
+    // Disallow revalidation while the content is still pending. This is
+    // sub-optimal because it will trigger a new load of the same resource.
+    if (!content_->IsLoaded()) {
+      return false;
+    }
+  }
+  return TextResource::CanUseCacheValidator();
+}
+
 void SVGDocumentResource::NotifyStartLoad() {
   TextResource::NotifyStartLoad();
   CHECK_EQ(GetStatus(), ResourceStatus::kPending);
diff --git a/third_party/blink/renderer/core/loader/resource/svg_document_resource.h b/third_party/blink/renderer/core/loader/resource/svg_document_resource.h
index d646411..f5ee961c 100644
--- a/third_party/blink/renderer/core/loader/resource/svg_document_resource.h
+++ b/third_party/blink/renderer/core/loader/resource/svg_document_resource.h
@@ -26,6 +26,8 @@
                       const TextResourceDecoderOptions&,
                       SVGResourceDocumentContent*);
 
+  bool CanUseCacheValidator() const override;
+
   void NotifyStartLoad() override;
   void Finish(base::TimeTicks finish_time,
               base::SingleThreadTaskRunner*) override;
diff --git a/third_party/blink/renderer/core/page/focus_controller.cc b/third_party/blink/renderer/core/page/focus_controller.cc
index 778043d6..c0893d6 100644
--- a/third_party/blink/renderer/core/page/focus_controller.cc
+++ b/third_party/blink/renderer/core/page/focus_controller.cc
@@ -1359,6 +1359,25 @@
   return element;
 }
 
+namespace {
+ScopedFocusNavigation GetScopeFor(Element*& owner,
+                                  FocusController::OwnerMap& owner_map) {
+  ScopedFocusNavigation new_scope =
+      ScopedFocusNavigation::CreateFor(*owner, owner_map);
+  while (new_scope.Owner() == owner) {
+    // This can happen if a single element is both the root of a scope,
+    // *and* the owner of a scope. E.g. <slot popover>. See
+    // crbug.com/447888734.
+    owner = owner->parentElement();
+    if (!owner) {
+      break;
+    }
+    new_scope = ScopedFocusNavigation::CreateFor(*owner, owner_map);
+  }
+  return new_scope;
+}
+}  // namespace
+
 Element* FindFocusableElementAcrossFocusScopesForward(
     ScopedFocusNavigation& scope,
     FocusController::OwnerMap& owner_map) {
@@ -1396,7 +1415,7 @@
     Element* owner = current_scope.Owner();
     if (!owner)
       break;
-    current_scope = ScopedFocusNavigation::CreateFor(*owner, owner_map);
+    current_scope = GetScopeFor(owner, owner_map);
     found = FindFocusableElementRecursivelyForward(current_scope, owner_map);
   }
   return FindFocusableElementDescendingDownIntoFrameDocument(
@@ -1435,7 +1454,7 @@
       found = owner;
       break;
     }
-    current_scope = ScopedFocusNavigation::CreateFor(*owner, owner_map);
+    current_scope = GetScopeFor(owner, owner_map);
     found = FindFocusableElementRecursivelyBackward(current_scope, owner_map);
   }
   return FindFocusableElementDescendingDownIntoFrameDocument(
diff --git a/third_party/blink/renderer/core/paint/clip_path_clipper.cc b/third_party/blink/renderer/core/paint/clip_path_clipper.cc
index 9f8f05c..cfd7fb7 100644
--- a/third_party/blink/renderer/core/paint/clip_path_clipper.cc
+++ b/third_party/blink/renderer/core/paint/clip_path_clipper.cc
@@ -283,6 +283,12 @@
     return true;
   }
 
+  // TODO(crbug.com/449152897): Backdrop-filter and clip path paint worklet
+  // images are not rasterized correctly.
+  if (!layout_object.StyleRef().BackdropFilter().IsEmpty()) {
+    return true;
+  }
+
   return false;
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_resource_document_content_test.cc b/third_party/blink/renderer/core/svg/svg_resource_document_content_test.cc
index 559b7ea..defb366 100644
--- a/third_party/blink/renderer/core/svg/svg_resource_document_content_test.cc
+++ b/third_party/blink/renderer/core/svg/svg_resource_document_content_test.cc
@@ -152,6 +152,51 @@
   main_resource.Complete();
 }
 
+TEST_F(SVGResourceDocumentContentSimTest,
+       AsyncLoadCompleteCallbackRevalidationRace) {
+  SimRequest main_resource("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  main_resource.Complete("<!doctype html>");
+
+  // Setup response headers so that the resource will be revalidated.
+  SimRequest::Params revalidate_params;
+  revalidate_params.response_http_headers = {{"Cache-Control", "max-age=0"},
+                                             {"ETag", "foo"}};
+  SimSubresourceRequest svg_resource("https://example.com/resource.svg#root",
+                                     "image/svg+xml", revalidate_params);
+
+  // Make an initial request for 'resource.svg'.
+  GetDocument().body()->SetInnerHTMLWithoutTrustedTypes(
+      "<svg><use href='resource.svg#root'/></svg>");
+
+  String svg_resource_content(R"SVG(
+    <svg id="root" xmlns="http://www.w3.org/2000/svg">
+      <image href="data:image/gif;base64,R0lGODdhCQAJAKEAAO6C7v8A/6Ag8AAAACwAAAAACQAJAAACFISPaWLhLhh4UNIQG81zswiGIlgAADs="/>
+    </svg>)SVG");
+  svg_resource.Start();
+  svg_resource.Complete(svg_resource_content);
+
+  // Flush tasks on the "internal loading" task queue. IsolatedSVGDocumentHost
+  // will post/run the async-loading-complete callback on this task queue.
+  base::RunLoop run_loop;
+  GetDocument()
+      .GetTaskRunner(TaskType::kInternalLoading)
+      ->PostTask(FROM_HERE, run_loop.QuitClosure());
+  run_loop.Run();
+
+  // We don't expect the below to trigger a revalidation, but a new load, so
+  // setup the subresource again.
+  SimSubresourceRequest svg_resource_second_load(
+      "https://example.com/resource.svg#root", "image/svg+xml",
+      revalidate_params);
+
+  // Make another request to the same resource.
+  GetDocument().body()->firstElementChild()->cloneNode(true);
+
+  svg_resource_second_load.Start();
+  svg_resource_second_load.Complete(svg_resource_content);
+}
+
 class SVGResourceDocumentContentTest : public PageTestBase {
  public:
   SVGResourceDocumentContentTest()
diff --git a/third_party/blink/renderer/core/url/dom_origin.cc b/third_party/blink/renderer/core/url/dom_origin.cc
index 0fbfbbe..4f3e127 100644
--- a/third_party/blink/renderer/core/url/dom_origin.cc
+++ b/third_party/blink/renderer/core/url/dom_origin.cc
@@ -4,7 +4,27 @@
 
 #include "third_party/blink/renderer/core/url/dom_origin.h"
 
+#include "third_party/blink/renderer/bindings/core/v8/binding_security.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_html_anchor_element.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_html_area_element.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_location.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_message_event.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_origin.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_url.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_window.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_worker_global_scope.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_worker_location.h"
+#include "third_party/blink/renderer/core/events/message_event.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/frame/location.h"
+#include "third_party/blink/renderer/core/html/html_anchor_element.h"
+#include "third_party/blink/renderer/core/html/html_area_element.h"
+#include "third_party/blink/renderer/core/url/dom_url.h"
+#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
+#include "third_party/blink/renderer/core/workers/worker_location.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -41,6 +61,130 @@
   return MakeGarbageCollected<DOMOrigin>(SecurityOrigin::Create(url));
 }
 
+// static
+DOMOrigin* DOMOrigin::from(ScriptState* script_state,
+                           ScriptValue script_value,
+                           ExceptionState& exception_state) {
+  v8::Isolate* isolate = script_state->GetIsolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Local<v8::Value> v8_value = script_value.V8Value();
+
+  // If we're given a string, try to parse it as a URL, and extract an origin
+  // from it if possible.
+  if (v8_value->IsString()) {
+    String serialized_url = ToCoreString(isolate, v8_value.As<v8::String>());
+
+    KURL parsed_url(serialized_url);
+    if (!parsed_url.IsValid()) {
+      exception_state.ThrowTypeError(
+          "The string provided cannot be parsed as a serialized URL.");
+      return nullptr;
+    }
+    return MakeGarbageCollected<DOMOrigin>(SecurityOrigin::Create(parsed_url));
+  }
+
+  // If it's not a string, check whether it's an object. If it's not, throw an
+  // exception to exit early.
+  if (!v8_value->IsObject()) {
+    exception_state.ThrowTypeError(
+        "An origin cannot be extracted from the given parameter.");
+    return nullptr;
+  }
+
+  // Given that we're dealing with an object, walk through all the types from
+  // which we know how to construct an Origin. These include:
+  //
+  // * ExtendableMessageEvent
+  // * HTMLHyperlinkElementUtils
+  // * Location
+  // * MessageEvent
+  // * Origin
+  // * URL
+  // * WindowOrWorkerGlobalScope
+  // * WorkerLocation
+  //
+  // For each of these platform object interfaces, we'll check to see whether
+  // the value we've been given is an instance of the underlying type we're
+  // going to poke at, and extract an origin if so. If nothing matches, we'll
+  // fall through to throw a `TypeError`.
+  v8::Local<v8::Object> v8_object = v8_value.As<v8::Object>();
+
+  // ExtendableMessageEvent
+  //
+  // TODO(mkwst): This is implemented as a module, which might mean we need to
+  // move `Origin` object itself elsewhere to enable inclusion. Leaving this
+  // unimplemented for the moment.
+
+  // HTMLHyperlinkElementUtils (as `HTMLAnchorElement` or `HTMLAreaElement`)
+  if (HTMLAnchorElement* el =
+          V8HTMLAnchorElement::ToWrappable(isolate, v8_object)) {
+    return MakeGarbageCollected<DOMOrigin>(SecurityOrigin::Create(el->Url()));
+  }
+  if (HTMLAreaElement* el =
+          V8HTMLAreaElement::ToWrappable(isolate, v8_object)) {
+    return MakeGarbageCollected<DOMOrigin>(SecurityOrigin::Create(el->Url()));
+  }
+
+  // Location is accessible cross-origin, so we need to be careful before
+  // handing out its Origin.
+  if (Location* location = V8Location::ToWrappable(isolate, v8_object)) {
+    if (BindingSecurity::ShouldAllowAccessTo(LocalDOMWindow::From(script_state),
+                                             location)) {
+      return MakeGarbageCollected<DOMOrigin>(
+          SecurityOrigin::CreateFromString(location->origin()));
+    }
+  }
+
+  // MessageEvent
+  if (MessageEvent* event = V8MessageEvent::ToWrappable(isolate, v8_object)) {
+    // TODO(434131026): We only have a serialized origin here, which means
+    // our handling of `null` is broken. We'll need to teach `MessageEvent`
+    // to hold a `SecurityOrigin` instead.
+    return MakeGarbageCollected<DOMOrigin>(
+        SecurityOrigin::CreateFromString(event->origin()));
+  }
+
+  // Origin
+  if (DOMOrigin* origin = V8Origin::ToWrappable(isolate, v8_object)) {
+    return MakeGarbageCollected<DOMOrigin>(origin->origin_);
+  }
+
+  // URL (as `DOMURL`)
+  if (DOMURL* dom_url = V8URL::ToWrappable(isolate, v8_object)) {
+    return MakeGarbageCollected<DOMOrigin>(
+        SecurityOrigin::Create(dom_url->Url()));
+  }
+
+  // WindowOrWorkerGlobalScope (as `LocalDOMWindow` or `WorkerGlobalScope`)
+  //
+  // Note that `Window` is accessible cross-origin, so we'll need security
+  // checks before generating an `Origin`.
+  if (DOMWindow* window = V8Window::ToWrappable(isolate, v8_object)) {
+    if (BindingSecurity::ShouldAllowAccessTo(LocalDOMWindow::From(script_state),
+                                             window) &&
+        window->IsLocalDOMWindow()) {
+      return MakeGarbageCollected<DOMOrigin>(
+          To<LocalDOMWindow>(window)->GetSecurityOrigin());
+    }
+  }
+  if (WorkerGlobalScope* worker =
+          V8WorkerGlobalScope::ToWrappable(isolate, v8_object)) {
+    return MakeGarbageCollected<DOMOrigin>(worker->GetSecurityOrigin());
+  }
+
+  // WorkerLocation
+  if (WorkerLocation* location =
+          V8WorkerLocation::ToWrappable(isolate, v8_object)) {
+    return MakeGarbageCollected<DOMOrigin>(
+        SecurityOrigin::Create(location->Url()));
+  }
+
+  // Finally, if it wasn't an object we know how to handle, throw an exception:
+  exception_state.ThrowTypeError(
+      "An origin cannot be extracted from the given parameter.");
+  return nullptr;
+}
+
 DOMOrigin* DOMOrigin::Create(const String& value,
                              ExceptionState& exception_state) {
   if (DOMOrigin* dom_origin = DOMOrigin::parse(value)) {
diff --git a/third_party/blink/renderer/core/url/dom_origin.h b/third_party/blink/renderer/core/url/dom_origin.h
index 2cc39e1b..17b6a54 100644
--- a/third_party/blink/renderer/core/url/dom_origin.h
+++ b/third_party/blink/renderer/core/url/dom_origin.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_URL_DOM_ORIGIN_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_URL_DOM_ORIGIN_H_
 
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
@@ -13,6 +14,7 @@
 namespace blink {
 
 class ExceptionState;
+class ScriptState;
 
 class CORE_EXPORT DOMOrigin final : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
@@ -34,6 +36,12 @@
   // Parses |value|, returning `null` if it isn't a validly serialized URL:
   static DOMOrigin* fromURL(const String& value);
 
+  // Converts |value| to an Origin, throwing an error if conversion isn't
+  // possible.
+  static DOMOrigin* from(ScriptState* script_state,
+                         ScriptValue value,
+                         ExceptionState& exception_state);
+
   String toJSON() const;
 
   bool opaque() const;
diff --git a/third_party/blink/renderer/core/url/origin.idl b/third_party/blink/renderer/core/url/origin.idl
index 133be2f8..66aa83c 100644
--- a/third_party/blink/renderer/core/url/origin.idl
+++ b/third_party/blink/renderer/core/url/origin.idl
@@ -13,6 +13,7 @@
 
   static Origin? parse(USVString serializedOrigin);
   static Origin? fromURL(USVString serializedURL);
+  [CallWith=ScriptState, RaisesException] static Origin from(any value);
 
   stringifier USVString toJSON();
 
diff --git a/third_party/blink/renderer/core/view_transition/view_transition_pseudo_element_base.h b/third_party/blink/renderer/core/view_transition/view_transition_pseudo_element_base.h
index dc9c5f1..fffb0881 100644
--- a/third_party/blink/renderer/core/view_transition/view_transition_pseudo_element_base.h
+++ b/third_party/blink/renderer/core/view_transition/view_transition_pseudo_element_base.h
@@ -41,6 +41,10 @@
 
   const AtomicString& GetContainingGroupName(const AtomicString& target) const;
 
+  const AtomicString& view_transition_name() const {
+    return GetPseudoArgument();
+  }
+
  protected:
   Vector<AtomicString> view_transition_class_;
   Member<const ViewTransitionStyleTracker> style_tracker_;
diff --git a/third_party/blink/renderer/core/view_transition/view_transition_style_tracker.cc b/third_party/blink/renderer/core/view_transition/view_transition_style_tracker.cc
index 6c8ddd3..3edc2bf 100644
--- a/third_party/blink/renderer/core/view_transition/view_transition_style_tracker.cc
+++ b/third_party/blink/renderer/core/view_transition/view_transition_style_tracker.cc
@@ -419,7 +419,9 @@
     // The view-transition must still be active.
     // See(crbug.com/444889294)
     ViewTransition* transition = ViewTransitionUtils::GetTransition(*this);
-    CHECK(transition && transition->IsGeneratingPseudo(*this));
+    if (!transition || !transition->IsGeneratingPseudo(*this)) {
+      return false;
+    }
 
     if (!ViewTransitionPseudoElementBase::CanGeneratePseudoElement(pseudo_id)) {
       return false;
diff --git a/third_party/blink/renderer/core/view_transition/view_transition_test.cc b/third_party/blink/renderer/core/view_transition/view_transition_test.cc
index cdd1cfeb9..0b7014b 100644
--- a/third_party/blink/renderer/core/view_transition/view_transition_test.cc
+++ b/third_party/blink/renderer/core/view_transition/view_transition_test.cc
@@ -898,7 +898,7 @@
     for (const auto& matched_rules : parent_resolver.PseudoElementRules()) {
       if (matched_rules->pseudo_id != test_case.pseudo_id)
         continue;
-      if (matched_rules->view_transition_name == "root") {
+      if (matched_rules->pseudo_argument == "root") {
         EXPECT_FALSE(found_rule_for_root);
         found_rule_for_root = true;
         continue;
@@ -912,7 +912,7 @@
     // Pseudo-elements which are generated for each tag should include the root
     // by default.
     EXPECT_EQ(found_rule_for_root, test_case.uses_tags);
-    EXPECT_EQ(matched_rules_for_pseudo->view_transition_name,
+    EXPECT_EQ(matched_rules_for_pseudo->pseudo_argument,
               test_case.uses_tags ? AtomicString("foo") : g_null_atom);
 
     auto pseudo_element_rules = matched_rules_for_pseudo->matched_rules;
diff --git a/third_party/blink/renderer/core/view_transition/view_transition_transition_element.cc b/third_party/blink/renderer/core/view_transition/view_transition_transition_element.cc
index 80c7e65..b06d3fca 100644
--- a/third_party/blink/renderer/core/view_transition/view_transition_transition_element.cc
+++ b/third_party/blink/renderer/core/view_transition/view_transition_transition_element.cc
@@ -17,17 +17,17 @@
   }
   VLOG(2) << std::string(indent, ' ') << node->DebugName();
   LogSubtree(node->GetPseudoElement(PseudoId::kPseudoIdViewTransitionImagePair,
-                                    node->view_transition_name()),
+                                    node->GetPseudoArgument()),
              indent + 2);
   LogSubtree(node->GetPseudoElement(PseudoId::kPseudoIdViewTransitionOld,
-                                    node->view_transition_name()),
+                                    node->GetPseudoArgument()),
              indent + 2);
   LogSubtree(node->GetPseudoElement(PseudoId::kPseudoIdViewTransitionNew,
-                                    node->view_transition_name()),
+                                    node->GetPseudoArgument()),
              indent + 2);
   LogSubtree(
       node->GetPseudoElement(PseudoId::kPseudoIdViewTransitionGroupChildren,
-                             node->view_transition_name()),
+                             node->GetPseudoArgument()),
       indent + 2);
   for (auto& name : style_tracker_->GetViewTransitionNames()) {
     LogSubtree(
@@ -65,7 +65,7 @@
   if (parent != this) {
     parent =
         parent->GetPseudoElement(PseudoId::kPseudoIdViewTransitionGroupChildren,
-                                 parent->view_transition_name());
+                                 parent->GetPseudoArgument());
   }
   return parent ? parent->GetPseudoElement(
                       PseudoId::kPseudoIdViewTransitionGroup, name)
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 624da9d6..32fe04b 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1223,13 +1223,36 @@
   return kDefaultBehavior;
 }
 
+bool AXNodeObject::ComputeIsIgnoredAsInsideInactiveScrollMarkerTab() {
+  Node* node = GetNode();
+  if (!node || !RuntimeEnabledFeatures::CSSScrollMarkerGroupModesEnabled()) {
+    return false;
+  }
+  if (node->IsCarouselPseudoElement()) {
+    // The carousel pseudo-elements should never be ignored.
+    return false;
+  }
+  if (IsOriginatingElementForInactiveScrollMarkerInTabsMode(node)) {
+    return true;
+  }
+  if (!ParentObject()) {
+    return false;
+  }
+  // As soon as one of the ancestors is the originating element for
+  // ::scroll-marker in tabs mode, we know this node is inside the originating
+  // element for ::scroll-marker in tabs mode, so we just propagate this info
+  // down to the children.
+  return ParentObject()
+      ->InsideOriginatingElementForInactiveScrollMarkerInTabsMode();
+}
+
 bool AXNodeObject::ComputeIsIgnored(IgnoredReasons* ignored_reasons) const {
   Node* node = GetNode();
 
-  // Originating element should be ignored in AX tree, if the ::scroll-marker
-  // is in tabs mode and not active.
-  if (RuntimeEnabledFeatures::CSSScrollMarkerGroupModesEnabled() &&
-      IsOriginatingElementForInactiveScrollMarkerInTabsMode(node)) {
+  // Everything (besides carousel pseudo-elements) inside and including the
+  // originating element of the
+  // ::scroll-marker is in tabs mode should be ignored in AX tree.
+  if (InsideOriginatingElementForInactiveScrollMarkerInTabsMode()) {
     if (ignored_reasons) {
       ignored_reasons->push_back(IgnoredReason(kAXInactiveCarouselTabContent));
     }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.h b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
index a7ff753..455ef2e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
@@ -81,6 +81,7 @@
   AXObjectInclusion ShouldIncludeBasedOnSemantics(
       IgnoredReasons* = nullptr) const;
   bool ComputeIsIgnored(IgnoredReasons*) const override;
+  bool ComputeIsIgnoredAsInsideInactiveScrollMarkerTab() override;
   ax::mojom::blink::Role DetermineRoleValue() override;
   ax::mojom::blink::Role NativeRoleIgnoringAria() const override;
   void AlterSliderOrSpinButtonValue(bool increase);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index feffc9bc1..efd44a5 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -3701,17 +3701,23 @@
   bool is_aria_hidden = ComputeIsAriaHidden();
   bool is_in_menu_list_subtree = ComputeIsInMenuListSubtree();
   bool is_descendant_of_disabled_node = ComputeIsDescendantOfDisabledNode();
+  bool is_ignored_as_inside_inactive_scroll_marker_tab =
+      ComputeIsIgnoredAsInsideInactiveScrollMarkerTab();
   bool is_changing_inherited_values = false;
   if (cached_is_inert_ != is_inert ||
       cached_is_aria_hidden_ != is_aria_hidden ||
       cached_is_in_menu_list_subtree_ != is_in_menu_list_subtree ||
       cached_is_descendant_of_disabled_node_ !=
-          is_descendant_of_disabled_node) {
+          is_descendant_of_disabled_node ||
+      cached_is_ignored_as_inside_inactive_scroll_marker_tab_ !=
+          is_ignored_as_inside_inactive_scroll_marker_tab) {
     is_changing_inherited_values = true;
     cached_is_inert_ = is_inert;
     cached_is_aria_hidden_ = is_aria_hidden;
     cached_is_in_menu_list_subtree_ = is_in_menu_list_subtree;
     cached_is_descendant_of_disabled_node_ = is_descendant_of_disabled_node;
+    cached_is_ignored_as_inside_inactive_scroll_marker_tab_ =
+        is_ignored_as_inside_inactive_scroll_marker_tab;
   }
 
   // Must be after inert computation, because focusability depends on that, but
@@ -4306,6 +4312,11 @@
 bool AXObject::ComputeIsIgnoredButIncludedInTree() {
   CHECK(!IsDetached());
 
+  // Nothing inside an inactive scroll marker's tab is included in the tree.
+  if (InsideOriginatingElementForInactiveScrollMarkerInTabsMode()) {
+    return false;
+  }
+
   // If an inline text box is ignored, it is never included in the tree.
   if (IsAXInlineTextBox()) {
     return false;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 4df2e67..02745adc 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -1022,6 +1022,14 @@
   const AXObject* AncestorMenuListOption() const;
   const AXObject* AncestorMenuList() const;
 
+  // Helper for scroll markers in tabs mode.
+  virtual bool ComputeIsIgnoredAsInsideInactiveScrollMarkerTab() {
+    return false;
+  }
+  bool InsideOriginatingElementForInactiveScrollMarkerInTabsMode() const {
+    return cached_is_ignored_as_inside_inactive_scroll_marker_tab_;
+  }
+
   // ARIA live-region features.
   bool IsLiveRegionRoot() const;  // Any live region, including polite="off".
   bool IsActiveLiveRegionRoot() const;  // Live region that is not polite="off".
@@ -1691,6 +1699,9 @@
   bool cached_is_descendant_of_disabled_node_ : 1 = false;
   bool cached_can_set_focus_attribute_ : 1 = false;
   bool cached_is_in_menu_list_subtree_ : 1 = false;
+  // True if this object is inside the originating element for an inactive
+  // ::scroll-marker in tabs mode.
+  bool cached_is_ignored_as_inside_inactive_scroll_marker_tab_ : 1 = false;
   bool always_load_inline_text_boxes_ : 1 = false;  // Used for Android only.
   std::optional<bool> cached_is_on_screen_;
 
diff --git a/third_party/blink/renderer/modules/cache_storage/window_cache_storage.idl b/third_party/blink/renderer/modules/cache_storage/window_or_worker_global_scope_cache_storage.idl
similarity index 86%
rename from third_party/blink/renderer/modules/cache_storage/window_cache_storage.idl
rename to third_party/blink/renderer/modules/cache_storage/window_or_worker_global_scope_cache_storage.idl
index 458b50bf..03638a27 100644
--- a/third_party/blink/renderer/modules/cache_storage/window_cache_storage.idl
+++ b/third_party/blink/renderer/modules/cache_storage/window_or_worker_global_scope_cache_storage.idl
@@ -5,6 +5,6 @@
 // https://w3c.github.io/ServiceWorker/#self-caches
 [
     ImplementedAs=GlobalCacheStorage
-] partial interface Window {
+] partial interface mixin WindowOrWorkerGlobalScope {
     [SecureContext, MeasureAs=GlobalCacheStorage, RaisesException] readonly attribute CacheStorage caches;
 };
diff --git a/third_party/blink/renderer/modules/cache_storage/worker_cache_storage.idl b/third_party/blink/renderer/modules/cache_storage/worker_cache_storage.idl
deleted file mode 100644
index 7bd7fcb..0000000
--- a/third_party/blink/renderer/modules/cache_storage/worker_cache_storage.idl
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2015 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// https://w3c.github.io/ServiceWorker/#self-caches
-[
-    ImplementedAs=GlobalCacheStorage
-] partial interface WorkerGlobalScope {
-    [SecureContext, MeasureAs=GlobalCacheStorage, RaisesException] readonly attribute CacheStorage caches;
-};
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index ae20b1c..d019fe8c 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -272,7 +272,7 @@
       (!SharedGpuContext::IsGpuCompositingEnabled() &&
        SharedGpuContext::SharedImageInterfaceProvider())) {
     RestoreGuard context_is_being_restored(*this);
-    if (GetOrCreateCanvas2DResourceProvider()) {
+    if (GetOrCreateResourceProvider()) {
       try_restore_context_event_timer_.Stop();
       DispatchContextRestoredEvent(nullptr);
       return;
@@ -592,7 +592,7 @@
     return;
   }
 
-  if (isContextLost() || !CanCreateCanvas2dResourceProvider()) [[unlikely]] {
+  if (isContextLost() || !CanCreateResourceProvider()) [[unlikely]] {
     return;
   }
 
@@ -806,7 +806,7 @@
 BaseRenderingContext2D::PaintRenderingResultsToSnapshot(
     SourceDrawingBuffer source_buffer,
     FlushReason reason) {
-  if (!IsCanvas2DResourceProviderValid()) {
+  if (!IsResourceProviderValid()) {
     return nullptr;
   }
 
@@ -815,6 +815,10 @@
   return provider->Snapshot(reason);
 }
 
+bool BaseRenderingContext2D::IsResourceProviderValid() {
+  return GetResourceProvider() && GetResourceProvider()->IsValid();
+}
+
 void BaseRenderingContext2D::WillUseCurrentFont() const {
   if (HTMLCanvasElement* canvas = HostAsHTMLCanvasElement();
       canvas != nullptr) {
@@ -1477,7 +1481,7 @@
   // accelerated SharedImage provider, we won't be able to transfer the canvas.
   // In that case, WebGPU access is not possible.
   CanvasResourceProviderSharedImage* provider =
-      GetOrCreateCanvas2DResourceProvider()->AsSharedImageProvider();
+      GetOrCreateResourceProvider()->AsSharedImageProvider();
   if (!provider || !provider->IsAccelerated()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "Unable to transfer canvas to GPU.");
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
index 0364108..6062901a 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
@@ -165,9 +165,8 @@
   // before `transferToGPUTexture` is first called.
   V8GPUTextureFormat getTextureFormat() const;
 
-  virtual bool CanCreateCanvas2dResourceProvider() = 0;
-  virtual bool IsCanvas2DResourceProviderValid() { NOTREACHED(); }
-  virtual CanvasResourceProvider* GetOrCreateCanvas2DResourceProvider() = 0;
+  virtual bool CanCreateResourceProvider() = 0;
+  virtual CanvasResourceProvider* GetOrCreateResourceProvider() = 0;
 
   String lang() const;
   void setLang(const String&);
@@ -251,6 +250,8 @@
     on_restore_failed_callback_for_testing_ = std::move(callback);
   }
 
+  bool IsResourceProviderValid();
+
   HeapTaskRunnerTimer<BaseRenderingContext2D>
       dispatch_context_lost_event_timer_;
   HeapTaskRunnerTimer<BaseRenderingContext2D>
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d_test.cc
index a7f1613..41af4978 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d_test.cc
@@ -93,7 +93,7 @@
   int Width() const override { return 300; }
   int Height() const override { return 300; }
 
-  bool CanCreateCanvas2dResourceProvider() override { return false; }
+  bool CanCreateResourceProvider() override { return false; }
 
   RespectImageOrientationEnum RespectImageOrientation() const override {
     return kRespectImageOrientation;
@@ -175,7 +175,7 @@
     return nullptr;
   }
 
-  CanvasResourceProvider* GetOrCreateCanvas2DResourceProvider() override {
+  CanvasResourceProvider* GetOrCreateResourceProvider() override {
     return nullptr;
   }
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
index 7f3ae94..6d1b43a 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -442,7 +442,7 @@
     }
   } else {
     // If we have no provider, try creating one.
-    provider = GetOrCreateCanvas2DResourceProvider();
+    provider = GetOrCreateResourceProvider();
     if (provider == nullptr) [[unlikely]] {
       return nullptr;
     }
@@ -690,16 +690,12 @@
 CanvasRenderingContext2D::PaintRenderingResultsToResource(
     SourceDrawingBuffer source_buffer,
     FlushReason reason) {
-  if (!IsCanvas2DResourceProviderValid()) {
+  if (!IsResourceProviderValid()) {
     return nullptr;
   }
   return resource_provider_->ProduceCanvasResource(reason);
 }
 
-bool CanvasRenderingContext2D::IsCanvas2DResourceProviderValid() {
-  return resource_provider_ && resource_provider_->IsValid();
-}
-
 const std::optional<cc::PaintRecord>&
 CanvasRenderingContext2D::GetLastRecordingForCanvas2D() {
   auto* provider = GetResourceProvider();
@@ -709,8 +705,8 @@
   return provider->LastRecording();
 }
 
-bool CanvasRenderingContext2D::CanCreateCanvas2dResourceProvider() {
-  return GetOrCreateCanvas2DResourceProvider();
+bool CanvasRenderingContext2D::CanCreateResourceProvider() {
+  return GetOrCreateResourceProvider();
 }
 
 scoped_refptr<StaticBitmapImage> blink::CanvasRenderingContext2D::GetImage(
@@ -720,7 +716,7 @@
         GetHibernationHandler()->GetImage());
   }
 
-  if (!IsCanvas2DResourceProviderValid()) {
+  if (!IsResourceProviderValid()) {
     return nullptr;
   }
 
@@ -934,7 +930,7 @@
   // TODO(crbug.com/40280152): Analyze whether this call is redundant (i.e.,
   // whether the CRP is guaranteed to always be present).
   if (canvas() && canvas()->LowLatencyEnabled() && canvas()->IsDirty()) {
-    GetOrCreateCanvas2DResourceProvider();
+    GetOrCreateResourceProvider();
   }
 }
 
@@ -1050,7 +1046,7 @@
   }
 
   if (page_is_visible && IsHibernating()) {
-    GetOrCreateCanvas2DResourceProvider();  // Rude awakening
+    GetOrCreateResourceProvider();  // Rude awakening
   }
 
   if (!element->IsPageVisible()) {
@@ -1321,7 +1317,7 @@
 }
 
 CanvasResourceProvider*
-CanvasRenderingContext2D::GetOrCreateCanvas2DResourceProvider() {
+CanvasRenderingContext2D::GetOrCreateResourceProvider() {
   HTMLCanvasElement* const element = canvas();
   if (!element) [[unlikely]] {
     return nullptr;
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
index 6d4c4b9..d8691cc 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
@@ -183,13 +183,12 @@
   scoped_refptr<CanvasResource> PaintRenderingResultsToResource(
       SourceDrawingBuffer source_buffer,
       FlushReason reason) override;
-  bool IsCanvas2DResourceProviderValid() override;
   const std::optional<cc::PaintRecord>& GetLastRecordingForCanvas2D() override;
 
   int Width() const final;
   int Height() const final;
 
-  bool CanCreateCanvas2dResourceProvider() final;
+  bool CanCreateResourceProvider() final;
 
   RespectImageOrientationEnum RespectImageOrientation() const final;
 
@@ -271,7 +270,7 @@
     return identifiability_study_helper_.encountered_partially_digested_image();
   }
 
-  CanvasResourceProvider* GetOrCreateCanvas2DResourceProvider() override;
+  CanvasResourceProvider* GetOrCreateResourceProvider() override;
   void SetCanvas2DResourceProviderForTesting(
       std::unique_ptr<CanvasResourceProvider> provider,
       const gfx::Size& size);
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_settings.idl b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_settings.idl
index 3770998..74cf39c13 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_settings.idl
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_settings.idl
@@ -7,6 +7,7 @@
     boolean alpha = true;
     boolean desynchronized = false;
     PredefinedColorSpace colorSpace = "srgb";
+    CanvasToneMapping toneMapping = {};
     [RuntimeEnabled=CanvasFloatingPoint] CanvasPixelFormat colorType = "unorm8";
     boolean willReadFrequently = false;
 };
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
index 6529b43..73ecaf4 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
@@ -386,9 +386,9 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   std::unique_ptr<frame_test_helpers::WebViewHelper> web_view_helper_;
   Persistent<HTMLCanvasElement> canvas_element_;
+  ScopedMemoryCacheForTesting scoped_memory_cache_;
 
  private:
-  Persistent<MemoryCache> global_memory_cache_;
   std::unique_ptr<ScopedAccelerated2dCanvasForTest> allow_accelerated_;
 
   class WrapGradients final : public GarbageCollected<WrapGradients> {
@@ -450,7 +450,7 @@
           CanvasContextCreationAttributesCore::WillReadFrequently::kUndefined,
           canvas);
       static_cast<CanvasRenderingContext2D*>(canvas->RenderingContext())
-          ->GetOrCreateCanvas2DResourceProvider();
+          ->GetOrCreateResourceProvider();
       // Expect that at least the first 10 are accelerated. The exact number
       // depends on the feature params.
       if (i < 10) {
@@ -467,7 +467,9 @@
 INSTANTIATE_PAINT_TEST_SUITE_P(CanvasRenderingContext2DTestAccelerated);
 
 CanvasRenderingContext2DTestBase::CanvasRenderingContext2DTestBase()
-    : wrap_gradients_(MakeGarbageCollected<WrapGradients>()),
+    : scoped_memory_cache_(MakeGarbageCollected<MemoryCache>(
+          blink::scheduler::GetSingleThreadTaskRunnerForTesting())),
+      wrap_gradients_(MakeGarbageCollected<WrapGradients>()),
       opaque_bitmap_(gfx::Size(10, 10), kOpaqueBitmap),
       alpha_bitmap_(gfx::Size(10, 10), kTransparentBitmap) {}
 
@@ -544,10 +546,6 @@
                                exception_state);
   EXPECT_FALSE(exception_state.HadException());
   AlphaGradient() = alpha_gradient;
-
-  global_memory_cache_ =
-      ReplaceMemoryCacheForTesting(MakeGarbageCollected<MemoryCache>(
-          blink::scheduler::GetSingleThreadTaskRunnerForTesting()));
 }
 
 void CanvasRenderingContext2DTestBase::TearDown() {
@@ -555,8 +553,6 @@
   ThreadState::Current()->CollectAllGarbageForTesting(
       ThreadState::StackState::kNoHeapPointers);
 
-  ReplaceMemoryCacheForTesting(global_memory_cache_.Release());
-
   // Tear down WebViewHelper because we override Platform in some tests which
   // must be torn down after WebViewHelper.
   web_view_helper_ = nullptr;
@@ -644,7 +640,7 @@
   // Install a CanvasResourceProvider that is accelerated and supports direct
   // compositing (the latter is necessary for
   // GetOrCreateCcLayerForCanvas2DIfNeeded() to succeed).
-  CHECK(context->GetOrCreateCanvas2DResourceProvider());
+  CHECK(context->GetOrCreateResourceProvider());
 
   // Put the host in GPU compositing mode.
   canvas_element.SetPreferred2DRasterMode(RasterModeHint::kPreferGPU);
@@ -683,11 +679,10 @@
 TEST_P(CanvasRenderingContext2DTest, NoRecreationOfResourceProviderAfterDraw) {
   CreateContext(kNonOpaque);
   uint32_t gen_id =
-      Context2D()->GetOrCreateCanvas2DResourceProvider()->ContentUniqueID();
+      Context2D()->GetOrCreateResourceProvider()->ContentUniqueID();
   Context2D()->fillRect(3, 3, 1, 1);
-  EXPECT_EQ(
-      gen_id,
-      Context2D()->GetOrCreateCanvas2DResourceProvider()->ContentUniqueID());
+  EXPECT_EQ(gen_id,
+            Context2D()->GetOrCreateResourceProvider()->ContentUniqueID());
 }
 
 TEST_P(CanvasRenderingContext2DTest,
@@ -711,7 +706,7 @@
   EXPECT_FALSE(!!CanvasElement().RateLimiter());
 
   CanvasElement().SetIsDisplayed(false);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_FALSE(!!CanvasElement().RateLimiter());
 
   // Invoking FinalizeFrame() twice should not result in rate limiting as the
@@ -826,7 +821,7 @@
   // The GetImage() call should have preserved the rasterization mode as well as
   // the validity of the resource.
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kCPU);
-  EXPECT_TRUE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_TRUE(Context2D()->IsResourceProviderValid());
 }
 
 TEST_P(CanvasRenderingContext2DTest, FillRect_FullCoverage) {
@@ -1555,8 +1550,7 @@
   DrawSomething();
   EXPECT_TRUE(Context2D()->getContextAttributes()->desynchronized());
   EXPECT_TRUE(CanvasElement().LowLatencyEnabled());
-  EXPECT_FALSE(
-      Context2D()->GetOrCreateCanvas2DResourceProvider()->IsSingleBuffered());
+  EXPECT_FALSE(Context2D()->GetOrCreateResourceProvider()->IsSingleBuffered());
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kCPU);
 }
 
@@ -1737,7 +1731,7 @@
       .gpu_memory_buffer_formats.Put(gfx::BufferFormat::BGRA_8888);
 
   CreateContext(kNonOpaque);
-  EXPECT_TRUE(Context2D()->GetOrCreateCanvas2DResourceProvider());
+  EXPECT_TRUE(Context2D()->GetOrCreateResourceProvider());
 
   // Draw to the canvas and verify that the canvas is composited.
   Context2D()->fillRect(0, 0, 1, 1);
@@ -1750,7 +1744,7 @@
   ScopedCanvas2dImageChromiumForTest canvas_2d_image_chromium(false);
 
   CreateContext(kNonOpaque);
-  EXPECT_TRUE(Context2D()->GetOrCreateCanvas2DResourceProvider());
+  EXPECT_TRUE(Context2D()->GetOrCreateResourceProvider());
 
   // Ensure that native support for BGRA GMBs is present, as otherwise
   // compositing will not occur irrespective of whether
@@ -1804,7 +1798,7 @@
 TEST_P(CanvasRenderingContext2DTestAccelerated, GetImage) {
   CreateContext(kNonOpaque);
 
-  ASSERT_TRUE(Context2D()->GetOrCreateCanvas2DResourceProvider());
+  ASSERT_TRUE(Context2D()->GetOrCreateResourceProvider());
   ASSERT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
 
   // Verify that CanvasRenderingContext2D::GetImage() creates an accelerated
@@ -1817,14 +1811,14 @@
   // The GetImage() call should have preserved the rasterization mode as well as
   // the validity of the resource.
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
-  EXPECT_TRUE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_TRUE(Context2D()->IsResourceProviderValid());
 }
 
 TEST_P(CanvasRenderingContext2DTestAccelerated,
        ReleaseLostTransferableResource) {
   CreateContext(kNonOpaque);
 
-  ASSERT_TRUE(Context2D()->GetOrCreateCanvas2DResourceProvider());
+  ASSERT_TRUE(Context2D()->GetOrCreateResourceProvider());
 
   // Invoking PrepareTransferableResource() has a precondition that a CC layer
   // is present.
@@ -1846,7 +1840,7 @@
        NoRegenerationOfTransferableResourceWhenAlreadyInCcLayer) {
   CreateContext(kNonOpaque);
 
-  ASSERT_TRUE(Context2D()->GetOrCreateCanvas2DResourceProvider());
+  ASSERT_TRUE(Context2D()->GetOrCreateResourceProvider());
 
   // Invoking PrepareTransferableResource() has a precondition that a CC layer
   // is present.
@@ -1874,7 +1868,7 @@
 TEST_P(CanvasRenderingContext2DTestAccelerated,
        ContextLostAndRestoredEventsAreEmittedAfterGPUContextLost) {
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   EXPECT_THAT(Context2D()->GetResourceProviderForTesting(), Pointee(IsValid()));
 
@@ -1904,7 +1898,7 @@
   CreateContextProvider(SetIsContextLost::kNotModifyValue);
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   EXPECT_THAT(Context2D()->GetResourceProviderForTesting(), Pointee(IsValid()));
 
@@ -1931,17 +1925,17 @@
        GetResourceProviderAfterContextLoss) {
   CreateContext(kNonOpaque);
 
-  EXPECT_TRUE(Context2D()->GetOrCreateCanvas2DResourceProvider());
+  EXPECT_TRUE(Context2D()->GetOrCreateResourceProvider());
 
   test_context_provider_->GetTestRasterInterface()->set_context_lost(true);
-  EXPECT_EQ(nullptr, Context2D()->GetOrCreateCanvas2DResourceProvider());
+  EXPECT_EQ(nullptr, Context2D()->GetOrCreateResourceProvider());
 }
 
 TEST_P(CanvasRenderingContext2DTestAccelerated,
        PrepareTransferableResourceAfterContextLoss) {
   CreateContext(kNonOpaque);
 
-  ASSERT_TRUE(Context2D()->GetOrCreateCanvas2DResourceProvider());
+  ASSERT_TRUE(Context2D()->GetOrCreateResourceProvider());
 
   // Invoking PrepareTransferableResource() has a precondition that a CC layer
   // is present.
@@ -1965,7 +1959,7 @@
        ReleaseLostTransferableResourceWithLostContext) {
   CreateContext(kNonOpaque);
 
-  ASSERT_TRUE(Context2D()->GetOrCreateCanvas2DResourceProvider());
+  ASSERT_TRUE(Context2D()->GetOrCreateResourceProvider());
 
   // Invoking PrepareTransferableResource() has a precondition that a CC layer
   // is present.
@@ -1997,10 +1991,10 @@
 
   test_context_provider_->GetTestRasterInterface()->set_context_lost(true);
 
-  ASSERT_TRUE(Context2D()->GetOrCreateCanvas2DResourceProvider());
+  ASSERT_TRUE(Context2D()->GetOrCreateResourceProvider());
 
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kCPU);
-  EXPECT_TRUE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_TRUE(Context2D()->IsResourceProviderValid());
 }
 
 TEST_P(CanvasRenderingContext2DTestAccelerated, GetImageAfterContextLoss) {
@@ -2011,7 +2005,7 @@
   // accelerated raster/compositing and a CC layer.
   ASSERT_TRUE(SetUpFullAccelerationAndCcLayer(CanvasElement(), Context2D()));
 
-  EXPECT_TRUE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_TRUE(Context2D()->IsResourceProviderValid());
   EXPECT_TRUE(Context2D()->GetImage(FlushReason::kTesting));
 
   test_context_provider_->GetTestRasterInterface()->set_context_lost(true);
@@ -2029,7 +2023,7 @@
   ASSERT_TRUE(SetUpFullAccelerationAndCcLayer(CanvasElement(), Context2D()));
 
   // The resource should start off valid.
-  EXPECT_TRUE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_TRUE(Context2D()->IsResourceProviderValid());
 
   viz::TransferableResource resource;
   viz::ReleaseCallback release_callback;
@@ -2039,7 +2033,7 @@
   // Losing the context should result in the resource becoming invalid and the
   // host being unable to produce a TransferableResource from it.
   test_context_provider_->GetTestRasterInterface()->set_context_lost(true);
-  EXPECT_FALSE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_FALSE(Context2D()->IsResourceProviderValid());
   EXPECT_FALSE(CanvasElement().PrepareTransferableResource(&resource,
                                                            &release_callback));
 
@@ -2048,7 +2042,7 @@
   // unit tests. This simulates what would happen when attempting to restore
   // while the GPU process is down.
   Context2D()->TryRestoreContextEvent(/*timer=*/nullptr);
-  EXPECT_FALSE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_FALSE(Context2D()->IsResourceProviderValid());
   EXPECT_FALSE(CanvasElement().PrepareTransferableResource(&resource,
                                                            &release_callback));
 }
@@ -2078,7 +2072,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
 
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
   ASSERT_FALSE(handler.IsHibernating());
@@ -2151,7 +2145,7 @@
       features::kCanvas2DHibernation};
   CreateContext(kNonOpaque);
   canvas_element_->SetSize(gfx::Size(64, 64));
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   ASSERT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
 
@@ -2184,7 +2178,7 @@
       {});
   CreateContext(kNonOpaque);
   canvas_element_->SetSize(gfx::Size(200, 200));
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   ASSERT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
 
@@ -2199,7 +2193,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
 
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
@@ -2220,7 +2214,7 @@
 
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kCPU);
   EXPECT_TRUE(handler.IsHibernating());
-  EXPECT_FALSE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_FALSE(Context2D()->IsResourceProviderValid());
 
   // Verify that coming to the foreground ends hibernation synchronously.
   {
@@ -2233,7 +2227,7 @@
         1);
     EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
     EXPECT_FALSE(handler.IsHibernating());
-    EXPECT_TRUE(Context2D()->IsCanvas2DResourceProviderValid());
+    EXPECT_TRUE(Context2D()->IsResourceProviderValid());
   }
 }
 
@@ -2242,7 +2236,7 @@
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   CanvasHibernationHandler* handler = Context2D()->GetHibernationHandler();
   viz::TestContextSupport* context_support = test_context_provider_->support();
 
@@ -2270,7 +2264,7 @@
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   CanvasHibernationHandler* handler = Context2D()->GetHibernationHandler();
   viz::TestContextSupport* context_support = test_context_provider_->support();
 
@@ -2314,7 +2308,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
 
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
@@ -2352,7 +2346,7 @@
 
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kCPU);
   EXPECT_TRUE(handler.IsHibernating());
-  EXPECT_FALSE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_FALSE(Context2D()->IsResourceProviderValid());
 
   // Verify that coming to the foreground ends hibernation synchronously.
   {
@@ -2365,7 +2359,7 @@
         1);
     EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
     EXPECT_FALSE(handler.IsHibernating());
-    EXPECT_TRUE(Context2D()->IsCanvas2DResourceProviderValid());
+    EXPECT_TRUE(Context2D()->IsResourceProviderValid());
   }
 }
 
@@ -2374,7 +2368,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
 
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
@@ -2395,7 +2389,7 @@
 
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kCPU);
   EXPECT_TRUE(handler.IsHibernating());
-  EXPECT_FALSE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_FALSE(Context2D()->IsResourceProviderValid());
 
   // Verify that tearing down the page ends hibernation synchronously.
   {
@@ -2417,7 +2411,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
 
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
@@ -2458,7 +2452,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   ASSERT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
 
   ASSERT_FALSE(Context2D()->IsHibernating());
@@ -2499,7 +2493,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
 
@@ -2522,7 +2516,7 @@
         CanvasHibernationHandler::HibernationEvent::
             kHibernationEndedWithFallbackToSW,
         1);
-    EXPECT_TRUE(Context2D()->IsCanvas2DResourceProviderValid());
+    EXPECT_TRUE(Context2D()->IsResourceProviderValid());
     EXPECT_FALSE(handler.IsHibernating());
     EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kCPU);
   }
@@ -2535,7 +2529,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   ASSERT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   ASSERT_FALSE(Context2D()->IsHibernating());
 
@@ -2565,7 +2559,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
 
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
@@ -2599,7 +2593,7 @@
         1);
     EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
     EXPECT_FALSE(handler.IsHibernating());
-    EXPECT_TRUE(Context2D()->IsCanvas2DResourceProviderValid());
+    EXPECT_TRUE(Context2D()->IsResourceProviderValid());
   }
 }
 
@@ -2614,7 +2608,7 @@
   // accelerated raster/compositing and a CC layer.
   ASSERT_TRUE(SetUpFullAccelerationAndCcLayer(CanvasElement(), Context2D()));
 
-  EXPECT_TRUE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_TRUE(Context2D()->IsResourceProviderValid());
 
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
   EXPECT_FALSE(handler.IsHibernating());
@@ -2647,7 +2641,7 @@
         1);
     EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
     EXPECT_FALSE(handler.IsHibernating());
-    EXPECT_FALSE(Context2D()->IsCanvas2DResourceProviderValid());
+    EXPECT_FALSE(Context2D()->IsResourceProviderValid());
   }
 }
 
@@ -2657,7 +2651,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
 
@@ -2685,7 +2679,7 @@
 
   EXPECT_FALSE(Context2D()->IsContextLost());
   EXPECT_FALSE(handler.IsHibernating());
-  EXPECT_TRUE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_TRUE(Context2D()->IsResourceProviderValid());
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
 }
 
@@ -2698,7 +2692,7 @@
   CreateContextProvider(SetIsContextLost::kNotModifyValue);
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
 
@@ -2740,7 +2734,7 @@
   CreateContextProvider(SetIsContextLost::kNotModifyValue);
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
 
@@ -2783,7 +2777,7 @@
   CreateContextProvider(SetIsContextLost::kNotModifyValue);
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
 
@@ -2821,7 +2815,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
 
@@ -2829,19 +2823,19 @@
   SetDocumentVisibility(GetDocument(), PageVisibilityState::kHidden);
   WaitForHibernation();
   EXPECT_TRUE(handler.IsHibernating());
-  EXPECT_FALSE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_FALSE(Context2D()->IsResourceProviderValid());
 
   // Recreate a provider to simulate background rendering.
   {
     base::HistogramTester histogram_tester;
-    Context2D()->GetOrCreateCanvas2DResourceProvider();
+    Context2D()->GetOrCreateResourceProvider();
     histogram_tester.ExpectUniqueSample(
         kCanvasHibernationEventHistogramName,
         CanvasHibernationHandler::HibernationEvent::
             kHibernationEndedWithSwitchToBackgroundRendering,
         1);
     EXPECT_FALSE(handler.IsHibernating());
-    EXPECT_TRUE(Context2D()->IsCanvas2DResourceProviderValid());
+    EXPECT_TRUE(Context2D()->IsResourceProviderValid());
   }
 }
 
@@ -2850,7 +2844,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
 
@@ -2877,7 +2871,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
 
@@ -2903,7 +2897,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
 
@@ -2935,7 +2929,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
 
@@ -3101,7 +3095,7 @@
   EXPECT_FALSE(CanvasElement().PrepareTransferableResource(&resource,
                                                            &release_callback));
   EXPECT_TRUE(handler.IsHibernating());
-  EXPECT_FALSE(Context2D()->IsCanvas2DResourceProviderValid());
+  EXPECT_FALSE(Context2D()->IsResourceProviderValid());
 }
 
 TEST_P(CanvasRenderingContext2DTestAccelerated,
@@ -3110,7 +3104,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
 
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
 
@@ -3148,7 +3142,7 @@
   scoped_feature_list.InitWithFeatures({features::kCanvas2DHibernation}, {});
 
   CreateContext(kNonOpaque);
-  ASSERT_TRUE(Context2D()->GetOrCreateCanvas2DResourceProvider());
+  ASSERT_TRUE(Context2D()->GetOrCreateResourceProvider());
   ASSERT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
 
   auto& handler = CHECK_DEREF(Context2D()->GetHibernationHandler());
@@ -3321,8 +3315,7 @@
   EXPECT_TRUE(Context2D()->getContextAttributes()->desynchronized());
   EXPECT_FALSE(Context2D()->getContextAttributes()->willReadFrequently());
   EXPECT_TRUE(CanvasElement().LowLatencyEnabled());
-  EXPECT_FALSE(
-      Context2D()->GetOrCreateCanvas2DResourceProvider()->IsSingleBuffered());
+  EXPECT_FALSE(Context2D()->GetOrCreateResourceProvider()->IsSingleBuffered());
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
 }
 
@@ -3331,7 +3324,7 @@
 
   CreateContext(kNonOpaque);
   // No need to set-up the layer bridge when testing low latency mode.
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
 
   gfx::Size visible_size(10, 10);
@@ -3366,7 +3359,7 @@
   CreateContext(kNonOpaque);
 
   // Ensure that the ResourceProvider and canvas are created.
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
 
   // Set a transform.
   Context2D()->translate(5, 0);
@@ -3386,7 +3379,7 @@
   CreateContext(kNonOpaque);
 
   // Ensure that the ResourceProvider and canvas are created.
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
 
   // Set a transform.
   Context2D()->translate(5, 0);
@@ -3428,7 +3421,7 @@
   ScopedCanvas2dLayersForTest layer_feature{/*enabled=*/true};
   CreateContext(kNonOpaque);
 
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
 
   NonThrowableExceptionState exception_state;
   Context2D()->fillRect(10, 10, 20, 20);
@@ -3469,7 +3462,7 @@
           CanvasContextCreationAttributesCore::WillReadFrequently::kUndefined,
           canvas);
       static_cast<CanvasRenderingContext2D*>(canvas->RenderingContext())
-          ->GetOrCreateCanvas2DResourceProvider();
+          ->GetOrCreateResourceProvider();
       EXPECT_TRUE(canvas->IsAccelerated());
       canvas->DisableAccelerationForCanvas2D();
     }
@@ -3485,7 +3478,7 @@
   CreateContext(
       kNonOpaque, kNormalLatency,
       CanvasContextCreationAttributesCore::WillReadFrequently::kUndefined);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   // Because a bunch of canvases had acceleration explicitly disabled, canvases
   // created with `kUndefined` should start with acceleration disabled.
   EXPECT_FALSE(CanvasElement().IsAccelerated());
@@ -3497,7 +3490,7 @@
   CreateContext(
       kNonOpaque, kNormalLatency,
       CanvasContextCreationAttributesCore::WillReadFrequently::kFalse);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   // Canvases created with `kFalse` should always start with acceleration
   // enabled regardless of how many canvases had acceleration disabled.
   EXPECT_TRUE(CanvasElement().IsAccelerated());
@@ -3508,7 +3501,7 @@
   CreateAlotOfCanvasesWithAccelerationExplicitlyDisabled();
   CreateContext(kNonOpaque, kNormalLatency,
                 CanvasContextCreationAttributesCore::WillReadFrequently::kTrue);
-  Context2D()->GetOrCreateCanvas2DResourceProvider();
+  Context2D()->GetOrCreateResourceProvider();
   // Canvases created with `kTrue` should always start with acceleration
   // disabled regardless of how many canvases had acceleration explicitly
   // disabled.
@@ -3552,15 +3545,14 @@
   EXPECT_FALSE(Context2D()->getContextAttributes()->willReadFrequently());
   EXPECT_TRUE(CanvasElement().LowLatencyEnabled());
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
-  EXPECT_TRUE(
-      Context2D()->GetOrCreateCanvas2DResourceProvider()->IsSingleBuffered());
+  EXPECT_TRUE(Context2D()->GetOrCreateResourceProvider()->IsSingleBuffered());
   auto frame1_resource =
-      Context2D()->GetOrCreateCanvas2DResourceProvider()->ProduceCanvasResource(
+      Context2D()->GetOrCreateResourceProvider()->ProduceCanvasResource(
           FlushReason::kTesting);
   EXPECT_TRUE(frame1_resource);
   DrawSomething();
   auto frame2_resource =
-      Context2D()->GetOrCreateCanvas2DResourceProvider()->ProduceCanvasResource(
+      Context2D()->GetOrCreateResourceProvider()->ProduceCanvasResource(
           FlushReason::kTesting);
   EXPECT_TRUE(frame2_resource);
   EXPECT_EQ(frame1_resource.get(), frame2_resource.get());
@@ -3596,15 +3588,14 @@
   EXPECT_FALSE(Context2D()->getContextAttributes()->willReadFrequently());
   EXPECT_TRUE(CanvasElement().LowLatencyEnabled());
   EXPECT_EQ(CanvasElement().GetRasterModeForCanvas2D(), RasterMode::kGPU);
-  EXPECT_TRUE(
-      Context2D()->GetOrCreateCanvas2DResourceProvider()->IsSingleBuffered());
+  EXPECT_TRUE(Context2D()->GetOrCreateResourceProvider()->IsSingleBuffered());
   auto frame1_resource =
-      Context2D()->GetOrCreateCanvas2DResourceProvider()->ProduceCanvasResource(
+      Context2D()->GetOrCreateResourceProvider()->ProduceCanvasResource(
           FlushReason::kTesting);
   EXPECT_TRUE(frame1_resource);
   DrawSomething();
   auto frame2_resource =
-      Context2D()->GetOrCreateCanvas2DResourceProvider()->ProduceCanvasResource(
+      Context2D()->GetOrCreateResourceProvider()->ProduceCanvasResource(
           FlushReason::kTesting);
   EXPECT_TRUE(frame2_resource);
   EXPECT_EQ(frame1_resource.get(), frame2_resource.get());
diff --git a/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_module.idl b/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_module.idl
index 172f229..81a8e557 100644
--- a/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_module.idl
+++ b/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_module.idl
@@ -26,6 +26,15 @@
     "float16",
 };
 
+enum CanvasToneMappingMode {
+    "standard", // default
+    "extended",
+};
+
+dictionary CanvasToneMapping {
+    CanvasToneMappingMode mode = "standard";
+};
+
 enum CanvasPowerPreference {
     "default",
     "low-power",
@@ -33,9 +42,9 @@
 };
 
 enum CanvasWillReadFrequently {
-  "true",
-  "false",
-  "undefined" // default
+    "true",
+    "false",
+    "undefined" // default
 };
 
 // The PermissiveDictionaryConversion extended attribute is needed to allow the
@@ -50,7 +59,8 @@
     // Canvas 2D attributes
     boolean alpha = true;  // Also used for WebGL.
     PredefinedColorSpace colorSpace = "srgb";
-    [RuntimeEnabled=CanvasFloatingPoint] CanvasPixelFormat colorType = "unorm8";
+    [RuntimeEnabled=CanvasToneMapping] CanvasToneMapping toneMapping = {};
+    CanvasPixelFormat colorType = "unorm8";
     CanvasWillReadFrequently willReadFrequently = "undefined";
 
     // WebGL attributes
diff --git a/third_party/blink/renderer/modules/canvas/imagebitmap/window_create_image_bitmap.idl b/third_party/blink/renderer/modules/canvas/imagebitmap/window_or_worker_global_scope_create_image_bitmap.idl
similarity index 94%
rename from third_party/blink/renderer/modules/canvas/imagebitmap/window_create_image_bitmap.idl
rename to third_party/blink/renderer/modules/canvas/imagebitmap/window_or_worker_global_scope_create_image_bitmap.idl
index a36b307..a4548df 100644
--- a/third_party/blink/renderer/modules/canvas/imagebitmap/window_create_image_bitmap.idl
+++ b/third_party/blink/renderer/modules/canvas/imagebitmap/window_or_worker_global_scope_create_image_bitmap.idl
@@ -15,7 +15,7 @@
 
 [
     ImplementedAs=ImageBitmapFactories
-] partial interface Window {
+] partial interface mixin WindowOrWorkerGlobalScope {
   // https://html.spec.whatwg.org/#windoworworkerglobalscope
   [CallWith=ScriptState, RaisesException] Promise<ImageBitmap> createImageBitmap(
       ImageBitmapSource imageBitmap, optional ImageBitmapOptions options = {});
diff --git a/third_party/blink/renderer/modules/canvas/imagebitmap/worker_create_image_bitmap.idl b/third_party/blink/renderer/modules/canvas/imagebitmap/worker_create_image_bitmap.idl
deleted file mode 100644
index 7f9435c8..0000000
--- a/third_party/blink/renderer/modules/canvas/imagebitmap/worker_create_image_bitmap.idl
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-[
-    ImplementedAs=ImageBitmapFactories
-] partial interface WorkerGlobalScope {
-  // https://html.spec.whatwg.org/#windoworworkerglobalscope
-  [CallWith=ScriptState, RaisesException] Promise<ImageBitmap> createImageBitmap(
-      ImageBitmapSource imageBitmap, optional ImageBitmapOptions options = {});
-  [CallWith=ScriptState, RaisesException] Promise<ImageBitmap> createImageBitmap(
-      ImageBitmapSource imageBitmap, long sx, long sy, long sw, long sh, optional ImageBitmapOptions options = {});
-};
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
index 6533c760..34b0a246e 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
@@ -133,7 +133,7 @@
 
   // Make sure surface is ready for painting: fix the rendering mode now
   // because it will be too late during the paint invalidation phase.
-  if (!GetOrCreateCanvas2DResourceProvider()) {
+  if (!GetOrCreateResourceProvider()) {
     return;
   }
   resource_provider_->FlushCanvas(reason);
@@ -156,16 +156,16 @@
   return Host()->Size().height();
 }
 
-bool OffscreenCanvasRenderingContext2D::CanCreateCanvas2dResourceProvider() {
+bool OffscreenCanvasRenderingContext2D::CanCreateResourceProvider() {
   const CanvasRenderingContextHost* const host = Host();
   if (host == nullptr || host->Size().IsEmpty()) [[unlikely]] {
     return false;
   }
-  return !!GetOrCreateCanvas2DResourceProvider();
+  return !!GetOrCreateResourceProvider();
 }
 
 CanvasResourceProvider*
-OffscreenCanvasRenderingContext2D::GetOrCreateCanvas2DResourceProvider() {
+OffscreenCanvasRenderingContext2D::GetOrCreateResourceProvider() {
   DCHECK(Host() && Host()->IsOffscreenCanvas());
   OffscreenCanvas* host = HostAsOffscreenCanvas();
   if (host == nullptr) [[unlikely]] {
@@ -295,7 +295,7 @@
 
 scoped_refptr<CanvasResource>
 OffscreenCanvasRenderingContext2D::ProduceCanvasResource(FlushReason reason) {
-  CanvasResourceProvider* provider = GetOrCreateCanvas2DResourceProvider();
+  CanvasResourceProvider* provider = GetOrCreateResourceProvider();
   if (!provider) {
     return nullptr;
   }
@@ -343,7 +343,7 @@
     return nullptr;
   }
 
-  if (!GetOrCreateCanvas2DResourceProvider()) {
+  if (!GetOrCreateResourceProvider()) {
     return nullptr;
   }
   scoped_refptr<StaticBitmapImage> image = GetImage(FlushReason::kTransfer);
@@ -385,8 +385,8 @@
 
 MemoryManagedPaintCanvas*
 OffscreenCanvasRenderingContext2D::GetOrCreatePaintCanvas() {
-  if (!is_valid_size_ || isContextLost() ||
-      !GetOrCreateCanvas2DResourceProvider()) [[unlikely]] {
+  if (!is_valid_size_ || isContextLost() || !GetOrCreateResourceProvider())
+      [[unlikely]] {
     return nullptr;
   }
   return GetPaintCanvas();
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
index 284965a..48d8f77 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
@@ -84,7 +84,7 @@
   int Width() const final;
   int Height() const final;
 
-  bool CanCreateCanvas2dResourceProvider() final;
+  bool CanCreateResourceProvider() final;
 
   // Offscreen canvas doesn't have any notion of image orientation.
   RespectImageOrientationEnum RespectImageOrientation() const final {
@@ -158,7 +158,7 @@
 
   scoped_refptr<CanvasResource> ProduceCanvasResource(FlushReason);
 
-  CanvasResourceProvider* GetOrCreateCanvas2DResourceProvider() override;
+  CanvasResourceProvider* GetOrCreateResourceProvider() override;
   std::unique_ptr<CanvasResourceProvider> ReplaceResourceProviderForCanvas2D(
       std::unique_ptr<CanvasResourceProvider>) override;
 
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_track_selector_list_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_track_selector_list_element.cc
index 6fae4e99..31c612b 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_track_selector_list_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_track_selector_list_element.cc
@@ -132,9 +132,11 @@
 
     auto i = To<Element>(target)->GetIntegralAttribute(SelectedTrackIdAttr());
     if (is_video_) {
-      MediaElement().videoTracks().AnonymousIndexedGetter(i)->setSelected(true);
+      MediaElement().videoTracks().AnonymousIndexedGetter(i)->setSelected(
+          true, TrackBase::ChangeSource::kUser);
     } else {
-      MediaElement().audioTracks().AnonymousIndexedGetter(i)->setEnabled(true);
+      MediaElement().audioTracks().AnonymousIndexedGetter(i)->setEnabled(
+          true, TrackBase::ChangeSource::kUser);
     }
 
     // Close the list.
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.cc
index f0dadb6..d1fbd6e 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.cc
@@ -85,23 +85,35 @@
     return;
   }
   ice_transport_channel()->AddGatheringStateCallback(
-      this, [this](webrtc::IceTransportInternal* transport) {
-        OnGatheringStateChanged(transport);
+      this, [that = weak_factory_.GetWeakPtr()](
+                webrtc::IceTransportInternal* transport) {
+        if (that) {
+          that->OnGatheringStateChanged(transport);
+        }
       });
   ice_transport_channel()->SubscribeCandidateGathered(
-      [this](webrtc::IceTransportInternal* transport,
-             const webrtc::Candidate& candidate) {
-        OnCandidateGathered(transport, candidate);
+      [that = weak_factory_.GetWeakPtr()](
+          webrtc::IceTransportInternal* transport,
+          const webrtc::Candidate& candidate) {
+        if (that) {
+          that->OnCandidateGathered(transport, candidate);
+        }
       });
   ice_transport_channel()->SubscribeIceTransportStateChanged(
-      [this](webrtc::IceTransportInternal* transport) {
-        OnStateChanged(transport);
+      [that = weak_factory_.GetWeakPtr()](
+          webrtc::IceTransportInternal* transport) {
+        if (that) {
+          that->OnStateChanged(transport);
+        }
       });
   ice_transport_channel()->SignalNetworkRouteChanged.connect(
       this, &IceTransportAdapterImpl::OnNetworkRouteChanged);
   ice_transport_channel()->SubscribeRoleConflict(
-      [this](webrtc::IceTransportInternal* transport) {
-        OnRoleConflict(transport);
+      [that = weak_factory_.GetWeakPtr()](
+          webrtc::IceTransportInternal* transport) {
+        if (that) {
+          that->OnRoleConflict(transport);
+        }
       });
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.h b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.h
index d78edf5..fcff11e 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_impl.h
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h"
 #include "third_party/webrtc/api/ice_transport_interface.h"
 
@@ -56,6 +57,7 @@
 
   const raw_ptr<Delegate> delegate_;
   webrtc::scoped_refptr<webrtc::IceTransportInterface> ice_transport_channel_;
+  base::WeakPtrFactory<IceTransportAdapterImpl> weak_factory_{this};
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc b/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc
index c74db11..9e836f76 100644
--- a/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc
+++ b/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc
@@ -910,7 +910,7 @@
   if (id == -1)
     return;
   SendPeerConnectionUpdate(
-      id, "signalingstatechange",
+      id, "onsignalingstatechange",
       StrCat({"\"", webrtc::PeerConnectionInterface::AsString(state).data(),
               "\""}));
 }
@@ -923,7 +923,7 @@
   if (id == -1)
     return;
   SendPeerConnectionUpdate(
-      id, "iceconnectionstatechange",
+      id, "oniceconnectionstatechange",
       StrCat({"\"", webrtc::PeerConnectionInterface::AsString(state).data(),
               "\""}));
 }
@@ -936,7 +936,7 @@
   if (id == -1)
     return;
   SendPeerConnectionUpdate(
-      id, "connectionstatechange",
+      id, "onconnectionstatechange",
       StrCat({"\"", webrtc::PeerConnectionInterface::AsString(state).data(),
               "\""}));
 }
@@ -949,7 +949,7 @@
   if (id == -1)
     return;
   SendPeerConnectionUpdate(
-      id, "icegatheringstatechange",
+      id, "onicegatheringstatechange",
       StrCat({"\"", webrtc::PeerConnectionInterface::AsString(state).data(),
               "\""}));
 }
@@ -1010,7 +1010,7 @@
   int id = GetLocalIDForHandler(pc_handler);
   if (id == -1)
     return;
-  SendPeerConnectionUpdate(id, "negotiationneeded", g_empty_string);
+  SendPeerConnectionUpdate(id, "onnegotiationneeded", g_empty_string);
 }
 
 void PeerConnectionTracker::TrackGetUserMedia(
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc
index 791ca420..938eb4b 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc
@@ -42,8 +42,9 @@
 
 RTCIceCandidate* ConvertToRtcIceCandidate(const webrtc::Candidate& candidate) {
   // The "" mid and sdpMLineIndex 0 are wrong, see https://crbug.com/1385446
+  const bool kIncludeUfrag = true;
   return RTCIceCandidate::Create(MakeGarbageCollected<RTCIceCandidatePlatform>(
-      String::FromUTF8(webrtc::SdpSerializeCandidate(candidate)), "", 0,
+      String::FromUTF8(candidate.ToCandidateAttribute(kIncludeUfrag)), "", 0,
       String(candidate.username()), String(candidate.url())));
 }
 
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 2040c1a..c7df952 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -1849,23 +1849,11 @@
   // context_provider_wrapper, so it's important to call that first as it can
   // invalidate the weak pointer.
   auto context_provider_wrapper = SharedGpuContext::ContextProviderWrapper();
-  auto size = Host()->Size();
-  auto format = GetSharedImageFormat();
-
-  bool using_webgl_image_chromium =
-      SharedGpuContext::MaySupportImageChromium() &&
-      (RuntimeEnabledFeatures::WebGLImageChromiumEnabled() ||
-       base::FeatureList::IsEnabled(features::kLowLatencyWebGLImageChromium));
-  bool using_swap_chain =
-      GetDrawingBuffer() && GetDrawingBuffer()->UsingSwapChain();
-  if (!using_swap_chain && !using_webgl_image_chromium) {
-    return false;
-  }
-
   if (!context_provider_wrapper) {
     return false;
   }
 
+  auto size = Host()->Size();
   const auto& capabilities =
       context_provider_wrapper->ContextProvider().GetCapabilities();
   if (size.width() > capabilities.max_texture_size ||
@@ -1873,21 +1861,12 @@
     return false;
   }
 
-  const auto& shared_image_capabilities =
-      context_provider_wrapper->ContextProvider()
-          .SharedImageInterface()
-          ->GetCapabilities();
-
-  bool shared_image_format_supported =
-      gpu::IsFormatSupportedForSIWithNativeBuffer(format, capabilities);
-
-  // Either swap_chain or shared image should be supported for this be used.
-  if (!shared_image_capabilities.shared_image_swap_chain &&
-      !shared_image_format_supported) {
+  auto* drawing_buffer = GetDrawingBuffer();
+  if (!drawing_buffer) {
     return false;
   }
 
-  return true;
+  return drawing_buffer->SupportsConcurrentReadWrite();
 }
 
 void WebGLRenderingContextBase::PageVisibilityChanged() {
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_configuration.idl b/third_party/blink/renderer/modules/webgpu/gpu_canvas_configuration.idl
index 80ab4a51..704c966 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_configuration.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_configuration.idl
@@ -9,15 +9,6 @@
     "premultiplied",
 };
 
-enum GPUCanvasToneMappingMode {
-    "standard",
-    "extended",
-};
-
-dictionary GPUCanvasToneMapping {
-  GPUCanvasToneMappingMode mode = "standard";
-};
-
 dictionary GPUCanvasConfiguration {
     required GPUDevice device;
     required GPUTextureFormat format;
@@ -25,5 +16,5 @@
     sequence<GPUTextureFormat> viewFormats = [];
     PredefinedColorSpace colorSpace = "srgb";
     GPUCanvasAlphaMode alphaMode = "opaque";
-    GPUCanvasToneMapping toneMapping = {};
+    CanvasToneMapping toneMapping = {};
 };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
index 00032d1..aef0592 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
@@ -9,10 +9,10 @@
 #include "gpu/command_buffer/client/raster_interface.h"
 #include "gpu/config/gpu_finch_features.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_union_htmlcanvaselement_offscreencanvas.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_canvas_tone_mapping.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_canvas_tone_mapping_mode.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_alpha_mode.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_configuration.h"
-#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_tone_mapping.h"
-#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_tone_mapping_mode.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_offscreen_rendering_context.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_rendering_context.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
@@ -563,9 +563,9 @@
   if (descriptor->hasToneMapping() && descriptor->toneMapping()->hasMode()) {
     tone_mapping_mode_ = descriptor->toneMapping()->mode().AsEnum();
     switch (tone_mapping_mode_) {
-      case V8GPUCanvasToneMappingMode::Enum::kStandard:
+      case V8CanvasToneMappingMode::Enum::kStandard:
         break;
-      case V8GPUCanvasToneMappingMode::Enum::kExtended:
+      case V8CanvasToneMappingMode::Enum::kExtended:
         hdr_metadata.extended_range.emplace(
             /*current_headroom=*/gfx::HdrMetadataExtendedRange::
                 kDefaultHdrHeadroom,
@@ -651,7 +651,7 @@
   configuration->setColorSpace(PredefinedColorSpaceToV8(color_space_));
   configuration->setAlphaMode(alpha_mode_);
 
-  GPUCanvasToneMapping* tone_mapping = GPUCanvasToneMapping::Create();
+  CanvasToneMapping* tone_mapping = CanvasToneMapping::Create();
   tone_mapping->setMode(tone_mapping_mode_);
   configuration->setToneMapping(tone_mapping);
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
index 2e05c2e8..3845e34a 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
@@ -6,8 +6,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_CANVAS_CONTEXT_H_
 
 #include "base/containers/heap_array.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_canvas_tone_mapping_mode.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_alpha_mode.h"
-#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_tone_mapping_mode.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context_factory.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -161,7 +161,7 @@
 
   PredefinedColorSpace color_space_ = PredefinedColorSpace::kSRGB;
   V8GPUCanvasAlphaMode::Enum alpha_mode_;
-  V8GPUCanvasToneMappingMode::Enum tone_mapping_mode_;
+  V8CanvasToneMappingMode::Enum tone_mapping_mode_;
   scoped_refptr<WebGPUTextureAlphaClearer> alpha_clearer_;
   scoped_refptr<WebGPUSwapBufferProvider> swap_buffers_;
 
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 009aede..9efd4e34 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -2238,7 +2238,6 @@
     "fonts/palette_interpolation_test.cc",
     "fonts/plain_text_node_test.cc",
     "fonts/script_run_iterator_test.cc",
-    "fonts/shaping/caching_word_shaper_test.cc",
     "fonts/shaping/font_features_test.cc",
     "fonts/shaping/frame_shape_cache_test.cc",
     "fonts/shaping/han_kerning_test.cc",
diff --git a/third_party/blink/renderer/platform/fonts/plain_text_node_test.cc b/third_party/blink/renderer/platform/fonts/plain_text_node_test.cc
index b9eb8f3c..1f793a34 100644
--- a/third_party/blink/renderer/platform/fonts/plain_text_node_test.cc
+++ b/third_party/blink/renderer/platform/fonts/plain_text_node_test.cc
@@ -7,9 +7,18 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/fonts/font.h"
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h"
 
 namespace blink {
 
+namespace {
+
+inline const ShapeResultTestInfo* TestInfo(const ShapeResult* result) {
+  return static_cast<const ShapeResultTestInfo*>(result);
+}
+
+}  // namespace
+
 class PlainTextNodeTest : public testing::Test {
  public:
   static std::pair<String, bool> NormalizeSpacesAndMaybeBidi(
@@ -19,7 +28,9 @@
   }
 
   static Font& TestFont() {
-    return *MakeGarbageCollected<Font>(FontDescription{}, nullptr);
+    FontDescription desc;
+    desc.SetLocale(LayoutLocale::Get(AtomicString("en")));
+    return *MakeGarbageCollected<Font>(desc, nullptr);
   }
 
   static PlainTextNode& CreatePlainTextNode(const TextRun& run,
@@ -283,6 +294,286 @@
   EXPECT_EQ(item5.Text(), u"456");
 }
 
+TEST_F(PlainTextNodeTest, SegmentLatinLeftToRight) {
+  TextRun text_run(base::byte_span_from_cstring("ABC DEF."));
+  PlainTextNode& node =
+      CreatePlainTextNode(text_run, kNormalizeSpace, kSupportsBidi);
+  unsigned start_index = 0;
+  unsigned num_glyphs = 0;
+  hb_script_t script = HB_SCRIPT_INVALID;
+
+  ASSERT_EQ(3u, node.ItemList().size());
+  const ShapeResult* result = node.ItemList()[0].GetShapeResult();
+  ASSERT_TRUE(
+      TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+  EXPECT_EQ(0u, start_index);
+  EXPECT_EQ(3u, num_glyphs);
+  EXPECT_EQ(HB_SCRIPT_LATIN, script);
+
+  result = node.ItemList()[1].GetShapeResult();
+  ASSERT_TRUE(
+      TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+  EXPECT_EQ(0u, start_index);
+  EXPECT_EQ(1u, num_glyphs);
+  EXPECT_EQ(HB_SCRIPT_LATIN, script);
+
+  result = node.ItemList()[2].GetShapeResult();
+  ASSERT_TRUE(
+      TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+  EXPECT_EQ(0u, start_index);
+  EXPECT_EQ(4u, num_glyphs);
+  EXPECT_EQ(HB_SCRIPT_LATIN, script);
+}
+
+TEST_F(PlainTextNodeTest, SegmentCommonAccentLeftToRight) {
+  const UChar kStr[] = {0x2F, 0x301, 0x2E, 0x20, 0x2E};
+  TextRun text_run{base::span(kStr)};
+  PlainTextNode& node =
+      CreatePlainTextNode(text_run, kNormalizeSpace, kSupportsBidi);
+  unsigned start_index = 0;
+  unsigned num_glyphs = 0;
+  hb_script_t script = HB_SCRIPT_INVALID;
+
+  ASSERT_EQ(3u, node.ItemList().size());
+  unsigned offset = 0;
+  const ShapeResult* result = node.ItemList()[0].GetShapeResult();
+  ASSERT_TRUE(
+      TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+  EXPECT_EQ(0u, offset + start_index);
+  EXPECT_EQ(3u, num_glyphs);
+#if U_ICU_VERSION_MAJOR_NUM >= 76
+  EXPECT_EQ(HB_SCRIPT_CHEROKEE, script);
+#else
+  EXPECT_EQ(HB_SCRIPT_COMMON, script);
+#endif
+  offset += result->NumCharacters();
+
+  result = node.ItemList()[1].GetShapeResult();
+  ASSERT_TRUE(
+      TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+  EXPECT_EQ(3u, offset + start_index);
+  EXPECT_EQ(1u, num_glyphs);
+  EXPECT_EQ(HB_SCRIPT_COMMON, script);
+  offset += result->NumCharacters();
+
+  result = node.ItemList()[2].GetShapeResult();
+  ASSERT_TRUE(
+      TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+  EXPECT_EQ(4u, offset + start_index);
+  EXPECT_EQ(1u, num_glyphs);
+  EXPECT_EQ(HB_SCRIPT_COMMON, script);
+  offset += result->NumCharacters();
+
+  ASSERT_EQ(5u, offset);
+}
+
+TEST_F(PlainTextNodeTest, SegmentCjkByCharacter) {
+  const UChar kStr[] = {0x56FD, 0x56FD,  // CJK Unified Ideograph
+                        'a',    'b',
+                        0x56FD,  // CJK Unified Ideograph
+                        'x',    'y',    'z',
+                        0x3042,   // HIRAGANA LETTER A
+                        0x56FD};  // CJK Unified Ideograph
+  TextRun text_run{base::span(kStr)};
+  PlainTextNode& node =
+      CreatePlainTextNode(text_run, kNormalizeSpace, kSupportsBidi);
+
+  ASSERT_EQ(7u, node.ItemList().size());
+  const ShapeResult* word_result = node.ItemList()[0].GetShapeResult();
+  EXPECT_EQ(1u, word_result->NumCharacters());
+  word_result = node.ItemList()[1].GetShapeResult();
+  EXPECT_EQ(1u, word_result->NumCharacters());
+
+  word_result = node.ItemList()[2].GetShapeResult();
+  EXPECT_EQ(2u, word_result->NumCharacters());
+
+  word_result = node.ItemList()[3].GetShapeResult();
+  EXPECT_EQ(1u, word_result->NumCharacters());
+
+  word_result = node.ItemList()[4].GetShapeResult();
+  EXPECT_EQ(3u, word_result->NumCharacters());
+
+  word_result = node.ItemList()[5].GetShapeResult();
+  EXPECT_EQ(1u, word_result->NumCharacters());
+  word_result = node.ItemList()[6].GetShapeResult();
+  EXPECT_EQ(1u, word_result->NumCharacters());
+}
+
+TEST_F(PlainTextNodeTest, SegmentCjkAndCommon) {
+  const UChar kStr[] = {'a',    'b',
+                        0xFF08,   // FULLWIDTH LEFT PARENTHESIS (script=common)
+                        0x56FD,   // CJK Unified Ideograph
+                        0x56FD,   // CJK Unified Ideograph
+                        0x56FD,   // CJK Unified Ideograph
+                        0x3002};  // IDEOGRAPHIC FULL STOP (script=common)
+  TextRun text_run{base::span(kStr)};
+  PlainTextNode& node =
+      CreatePlainTextNode(text_run, kNormalizeSpace, kSupportsBidi);
+
+  ASSERT_EQ(4u, node.ItemList().size());
+  const ShapeResult* word_result = node.ItemList()[0].GetShapeResult();
+  EXPECT_EQ(2u, word_result->NumCharacters());
+
+  word_result = node.ItemList()[1].GetShapeResult();
+  EXPECT_EQ(2u, word_result->NumCharacters());
+
+  word_result = node.ItemList()[2].GetShapeResult();
+  EXPECT_EQ(1u, word_result->NumCharacters());
+
+  word_result = node.ItemList()[3].GetShapeResult();
+  EXPECT_EQ(2u, word_result->NumCharacters());
+}
+
+TEST_F(PlainTextNodeTest, SegmentCjkAndInherit) {
+  const UChar kStr[] = {
+      0x304B,   // HIRAGANA LETTER KA
+      0x304B,   // HIRAGANA LETTER KA
+      0x3009,   // COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK
+      0x304B};  // HIRAGANA LETTER KA
+  TextRun text_run{base::span(kStr)};
+  PlainTextNode& node =
+      CreatePlainTextNode(text_run, kNormalizeSpace, kSupportsBidi);
+
+  ASSERT_EQ(3u, node.ItemList().size());
+  const ShapeResult* word_result = node.ItemList()[0].GetShapeResult();
+  EXPECT_EQ(1u, word_result->NumCharacters());
+
+  word_result = node.ItemList()[1].GetShapeResult();
+  EXPECT_EQ(2u, word_result->NumCharacters());
+
+  word_result = node.ItemList()[2].GetShapeResult();
+  EXPECT_EQ(1u, word_result->NumCharacters());
+}
+
+TEST_F(PlainTextNodeTest, SegmentCjkAndNonCjkCommon) {
+  const UChar kStr[] = {0x56FD,  // CJK Unified Ideograph
+                        ' '};
+  TextRun text_run{base::span(kStr)};
+  PlainTextNode& node =
+      CreatePlainTextNode(text_run, kNormalizeSpace, kSupportsBidi);
+
+  ASSERT_EQ(2u, node.ItemList().size());
+  const ShapeResult* word_result = node.ItemList()[0].GetShapeResult();
+  EXPECT_EQ(1u, word_result->NumCharacters());
+
+  word_result = node.ItemList()[1].GetShapeResult();
+  EXPECT_EQ(1u, word_result->NumCharacters());
+}
+
+TEST_F(PlainTextNodeTest, SegmentEmojiSequences) {
+  std::vector<std::string> test_strings = {
+      // A family followed by a couple with heart emoji sequence,
+      // the latter including a variation selector.
+      "\U0001f468\u200D\U0001f469\u200D\U0001f467\u200D\U0001f466\U0001f469"
+      "\u200D\u2764\uFE0F\u200D\U0001f48b\u200D\U0001f468",
+      // Pirate flag
+      "\U0001F3F4\u200D\u2620\uFE0F",
+      // Pilot, judge sequence
+      "\U0001f468\U0001f3fb\u200D\u2696\uFE0F\U0001f468\U0001f3fb\u200D\u2708"
+      "\uFE0F",
+      // Woman, Kiss, Man sequence
+      "\U0001f469\u200D\u2764\uFE0F\u200D\U0001f48b\u200D\U0001f468",
+      // Signs of horns with skin tone modifier
+      "\U0001f918\U0001f3fb",
+      // Man, dark skin tone, red hair
+      "\U0001f468\U0001f3ff\u200D\U0001f9b0"};
+
+  for (auto test_string : test_strings) {
+    String emoji_string = String::FromUTF8(test_string);
+    TextRun text_run(emoji_string);
+    PlainTextNode& node =
+        CreatePlainTextNode(text_run, kNormalizeSpace, kSupportsBidi);
+
+    ASSERT_EQ(1u, node.ItemList().size());
+    EXPECT_EQ(emoji_string.length(),
+              node.ItemList()[0].GetShapeResult()->NumCharacters())
+        << " Length mismatch for sequence: " << test_string;
+  }
+}
+
+TEST_F(PlainTextNodeTest, SegmentEmojiExtraZwjPrefix) {
+  // A ZWJ, followed by a family and a heart-kiss sequence.
+  const UChar kStr[] = {0x200D, 0xD83D, 0xDC68, 0x200D, 0xD83D, 0xDC69,
+                        0x200D, 0xD83D, 0xDC67, 0x200D, 0xD83D, 0xDC66,
+                        0xD83D, 0xDC69, 0x200D, 0x2764, 0xFE0F, 0x200D,
+                        0xD83D, 0xDC8B, 0x200D, 0xD83D, 0xDC68};
+  TextRun text_run{base::span(kStr)};
+  PlainTextNode& node =
+      CreatePlainTextNode(text_run, kNormalizeSpace, kSupportsBidi);
+
+  ASSERT_EQ(2u, node.ItemList().size());
+  const ShapeResult* word_result = node.ItemList()[0].GetShapeResult();
+  EXPECT_EQ(1u, word_result->NumCharacters());
+
+  word_result = node.ItemList()[1].GetShapeResult();
+  EXPECT_EQ(22u, word_result->NumCharacters());
+}
+
+TEST_F(PlainTextNodeTest, SegmentEmojiSubdivisionFlags) {
+  // Subdivision flags for Wales, Scotland, England.
+  const UChar kStr[] = {0xD83C, 0xDFF4, 0xDB40, 0xDC67, 0xDB40, 0xDC62, 0xDB40,
+                        0xDC77, 0xDB40, 0xDC6C, 0xDB40, 0xDC73, 0xDB40, 0xDC7F,
+                        0xD83C, 0xDFF4, 0xDB40, 0xDC67, 0xDB40, 0xDC62, 0xDB40,
+                        0xDC73, 0xDB40, 0xDC63, 0xDB40, 0xDC74, 0xDB40, 0xDC7F,
+                        0xD83C, 0xDFF4, 0xDB40, 0xDC67, 0xDB40, 0xDC62, 0xDB40,
+                        0xDC65, 0xDB40, 0xDC6E, 0xDB40, 0xDC67, 0xDB40, 0xDC7F};
+  TextRun text_run{base::span(kStr)};
+  PlainTextNode& node =
+      CreatePlainTextNode(text_run, kNormalizeSpace, kSupportsBidi);
+
+  ASSERT_EQ(1u, node.ItemList().size());
+  EXPECT_EQ(42u, node.ItemList()[0].GetShapeResult()->NumCharacters());
+}
+
+TEST_F(PlainTextNodeTest, SegmentCjkCommon) {
+  const UChar kStr[] = {0xFF08,   // FULLWIDTH LEFT PARENTHESIS (script=common)
+                        0xFF08,   // FULLWIDTH LEFT PARENTHESIS (script=common)
+                        0xFF08};  // FULLWIDTH LEFT PARENTHESIS (script=common)
+  TextRun text_run{base::span(kStr)};
+  PlainTextNode& node =
+      CreatePlainTextNode(text_run, kNormalizeSpace, kSupportsBidi);
+
+  ASSERT_EQ(1u, node.ItemList().size());
+  EXPECT_EQ(3u, node.ItemList()[0].GetShapeResult()->NumCharacters());
+}
+
+TEST_F(PlainTextNodeTest, SegmentCjkCommonAndNonCjk) {
+  const UChar kStr[] = {0xFF08,  // FULLWIDTH LEFT PARENTHESIS (script=common)
+                        'a', 'b'};
+  TextRun text_run{base::span(kStr)};
+  PlainTextNode& node =
+      CreatePlainTextNode(text_run, kNormalizeSpace, kSupportsBidi);
+
+  ASSERT_EQ(2u, node.ItemList().size());
+  const ShapeResult* word_result = node.ItemList()[0].GetShapeResult();
+  EXPECT_EQ(1u, word_result->NumCharacters());
+
+  word_result = node.ItemList()[1].GetShapeResult();
+  EXPECT_EQ(2u, word_result->NumCharacters());
+}
+
+TEST_F(PlainTextNodeTest, SegmentCjkSmallFormVariants) {
+  const UChar kStr[] = {0x5916,   // CJK UNIFIED IDEOGRPAH
+                        0xFE50};  // SMALL COMMA
+  TextRun text_run{base::span(kStr)};
+  PlainTextNode& node =
+      CreatePlainTextNode(text_run, kNormalizeSpace, kSupportsBidi);
+
+  ASSERT_EQ(1u, node.ItemList().size());
+  EXPECT_EQ(2u, node.ItemList()[0].GetShapeResult()->NumCharacters());
+}
+
+TEST_F(PlainTextNodeTest, SegmentHangulToneMark) {
+  const UChar kStr[] = {0xC740,   // HANGUL SYLLABLE EUN
+                        0x302E};  // HANGUL SINGLE DOT TONE MARK
+  TextRun text_run{base::span(kStr)};
+  PlainTextNode& node =
+      CreatePlainTextNode(text_run, kNormalizeSpace, kSupportsBidi);
+
+  ASSERT_EQ(1u, node.ItemList().size());
+  EXPECT_EQ(2u, node.ItemList()[0].GetShapeResult()->NumCharacters());
+}
+
 TEST_F(PlainTextNodeTest, Shape) {
   TextRun run("hello world");
   PlainTextNode& node =
diff --git a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h
index 137ff86..dd585c9 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h
@@ -72,9 +72,9 @@
     return NextWord(word_result);
   }
 
- private:
   const ShapeResult* ShapeWordWithoutSpacing(const TextRun&, const Font*);
 
+ private:
   const ShapeResult* ShapeWord(const TextRun&, const Font*);
 
   bool NextWord(const ShapeResult** word_result) {
diff --git a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc
index 6aba953a..8ce4311 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc
@@ -25,93 +25,17 @@
 
 #include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h"
 
-#include "third_party/blink/renderer/platform/fonts/character_range.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/shape_cache.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h"
-#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
-#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
 
 namespace blink {
 
-ShapeCache* CachingWordShaper::GetShapeCache() const {
-  return font_.GetShapeCache();
-}
-
-// Returns the total advance width of the TextRun run. If glyph_bounds
-// is specified it constructs on it the smallest bounding box covering all ink.
-float CachingWordShaper::Width(const TextRun& run, gfx::RectF* glyph_bounds) {
-  float width = 0;
-  const ShapeResult* word_result = nullptr;
-  CachingWordShapeIterator iterator(GetShapeCache(), run, &font_);
-  while (iterator.Next(&word_result)) {
-    if (word_result) {
-      // For every word_result we need to accumulate its width to adjust the
-      // glyph_bounds. When the word_result is in RTL we accumulate in the
-      // opposite direction (negative).
-      if (run.Rtl())
-        width -= word_result->Width();
-      if (glyph_bounds) {
-        gfx::RectF adjusted_bounds = word_result->ComputeInkBounds();
-        // Translate glyph bounds to the current glyph position which
-        // is the total width before this glyph.
-        adjusted_bounds.set_x(adjusted_bounds.x() + width);
-        glyph_bounds->Union(adjusted_bounds);
-      }
-      if (!run.Rtl())
-        width += word_result->Width();
-    }
-  }
-
-  if (run.Rtl()) {
-    // Finally, convert width back to positive if run is RTL.
-    width = -width;
-    if (glyph_bounds) {
-      glyph_bounds->set_x(glyph_bounds->x() + width);
-    }
-  }
-
-  return width;
-}
-
-static inline float ShapeResultsForRun(ShapeCache* shape_cache,
-                                       const Font* font,
-                                       const TextRun& run,
-                                       ShapeResultBuffer* results_buffer) {
-  CachingWordShapeIterator iterator(shape_cache, run, font);
-  const ShapeResult* word_result = nullptr;
-  float total_width = 0;
-  while (iterator.Next(&word_result)) {
-    if (word_result) {
-      total_width += word_result->Width();
-      results_buffer->AppendResult(std::move(word_result));
-    }
-  }
-  return total_width;
-}
-
-void CachingWordShaper::FillResultBuffer(const TextRun& run,
-                                         ShapeResultBuffer* buffer) {
-  DCHECK(buffer);
-  ShapeResultsForRun(GetShapeCache(), &font_, run, buffer);
-}
-
-CharacterRange CachingWordShaper::GetCharacterRange(const TextRun& run,
-                                                    unsigned from,
-                                                    unsigned to) {
-  ShapeResultBuffer buffer;
-  float total_width = ShapeResultsForRun(GetShapeCache(), &font_, run, &buffer);
-
-  return buffer.GetCharacterRange(run.ToStringView(), run.Direction(),
-                                  total_width, from, to);
-}
-
 GlyphData CachingWordShaper::EmphasisMarkGlyphData(
     const TextRun& emphasis_mark_run) const {
   ShapeResultBuffer buffer;
-  ShapeResultsForRun(GetShapeCache(), &font_, emphasis_mark_run, &buffer);
-
+  buffer.AppendResult(
+      CachingWordShapeIterator(font_.GetShapeCache(), emphasis_mark_run, &font_)
+          .ShapeWordWithoutSpacing(emphasis_mark_run, &font_));
   return buffer.EmphasisMarkGlyphData(font_.GetFontDescription());
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h
index fdc9e5a..a4a299d 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h
@@ -26,17 +26,13 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_CACHING_WORD_SHAPER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_CACHING_WORD_SHAPER_H_
 
-#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h"
-#include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
-#include "ui/gfx/geometry/rect_f.h"
 
 namespace blink {
 
-struct CharacterRange;
 class Font;
-class ShapeCache;
+class TextRun;
 struct GlyphData;
 
 class PLATFORM_EXPORT CachingWordShaper final {
@@ -48,16 +44,9 @@
   CachingWordShaper& operator=(const CachingWordShaper&) = delete;
   ~CachingWordShaper() = default;
 
-  float Width(const TextRun&, gfx::RectF* glyph_bounds);
-
-  void FillResultBuffer(const TextRun&, ShapeResultBuffer*);
-  CharacterRange GetCharacterRange(const TextRun&, unsigned from, unsigned to);
-
   GlyphData EmphasisMarkGlyphData(const TextRun&) const;
 
  private:
-  ShapeCache* GetShapeCache() const;
-
   const Font& font_;
 };
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper_test.cc b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper_test.cc
deleted file mode 100644
index f4bf04da2..0000000
--- a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper_test.cc
+++ /dev/null
@@ -1,391 +0,0 @@
-// Copyright 2014 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h"
-
-#include <memory>
-
-#include "base/test/task_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/fonts/font_cache.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h"
-#include "third_party/blink/renderer/platform/testing/font_test_base.h"
-
-namespace blink {
-
-class CachingWordShaperTest : public FontTestBase {
- protected:
-  void SetUp() override {
-    font_description.SetComputedSize(12.0);
-    font_description.SetLocale(LayoutLocale::Get(AtomicString("en")));
-    ASSERT_EQ(USCRIPT_LATIN, font_description.GetScript());
-    font_description.SetGenericFamily(FontDescription::kStandardFamily);
-
-    cache = MakeGarbageCollected<ShapeCache>();
-  }
-
-  FontCachePurgePreventer font_cache_purge_preventer;
-  FontDescription font_description;
-  Persistent<ShapeCache> cache;
-  unsigned start_index = 0;
-  unsigned num_glyphs = 0;
-  hb_script_t script = HB_SCRIPT_INVALID;
-};
-
-static inline const ShapeResultTestInfo* TestInfo(const ShapeResult* result) {
-  return static_cast<const ShapeResultTestInfo*>(result);
-}
-
-TEST_F(CachingWordShaperTest, LatinLeftToRightByWord) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-
-  TextRun text_run(base::byte_span_from_cstring("ABC DEF."));
-
-  const ShapeResult* result = nullptr;
-  CachingWordShapeIterator iterator(cache.Get(), text_run, font);
-  ASSERT_TRUE(iterator.Next(&result));
-  ASSERT_TRUE(
-      TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
-  EXPECT_EQ(0u, start_index);
-  EXPECT_EQ(3u, num_glyphs);
-  EXPECT_EQ(HB_SCRIPT_LATIN, script);
-
-  ASSERT_TRUE(iterator.Next(&result));
-  ASSERT_TRUE(
-      TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
-  EXPECT_EQ(0u, start_index);
-  EXPECT_EQ(1u, num_glyphs);
-  EXPECT_EQ(HB_SCRIPT_COMMON, script);
-
-  ASSERT_TRUE(iterator.Next(&result));
-  ASSERT_TRUE(
-      TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
-  EXPECT_EQ(0u, start_index);
-  EXPECT_EQ(4u, num_glyphs);
-  EXPECT_EQ(HB_SCRIPT_LATIN, script);
-
-  ASSERT_FALSE(iterator.Next(&result));
-}
-
-TEST_F(CachingWordShaperTest, CommonAccentLeftToRightByWord) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-
-  const UChar kStr[] = {0x2F, 0x301, 0x2E, 0x20, 0x2E};
-  TextRun text_run{base::span(kStr)};
-
-  unsigned offset = 0;
-  const ShapeResult* result = nullptr;
-  CachingWordShapeIterator iterator(cache.Get(), text_run, font);
-  ASSERT_TRUE(iterator.Next(&result));
-  ASSERT_TRUE(
-      TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
-  EXPECT_EQ(0u, offset + start_index);
-  EXPECT_EQ(3u, num_glyphs);
-#if U_ICU_VERSION_MAJOR_NUM >= 76
-  EXPECT_EQ(HB_SCRIPT_CHEROKEE, script);
-#else
-  EXPECT_EQ(HB_SCRIPT_COMMON, script);
-#endif
-  offset += result->NumCharacters();
-
-  ASSERT_TRUE(iterator.Next(&result));
-  ASSERT_TRUE(
-      TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
-  EXPECT_EQ(3u, offset + start_index);
-  EXPECT_EQ(1u, num_glyphs);
-  EXPECT_EQ(HB_SCRIPT_COMMON, script);
-  offset += result->NumCharacters();
-
-  ASSERT_TRUE(iterator.Next(&result));
-  ASSERT_TRUE(
-      TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
-  EXPECT_EQ(4u, offset + start_index);
-  EXPECT_EQ(1u, num_glyphs);
-  EXPECT_EQ(HB_SCRIPT_COMMON, script);
-  offset += result->NumCharacters();
-
-  ASSERT_EQ(5u, offset);
-  ASSERT_FALSE(iterator.Next(&result));
-}
-
-TEST_F(CachingWordShaperTest, SegmentCJKByCharacter) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-
-  const UChar kStr[] = {0x56FD, 0x56FD,  // CJK Unified Ideograph
-                        'a',    'b',
-                        0x56FD,  // CJK Unified Ideograph
-                        'x',    'y',    'z',
-                        0x3042,   // HIRAGANA LETTER A
-                        0x56FD};  // CJK Unified Ideograph
-  TextRun text_run{base::span(kStr)};
-
-  const ShapeResult* word_result = nullptr;
-  CachingWordShapeIterator iterator(cache.Get(), text_run, font);
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(1u, word_result->NumCharacters());
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(1u, word_result->NumCharacters());
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(2u, word_result->NumCharacters());
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(1u, word_result->NumCharacters());
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(3u, word_result->NumCharacters());
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(1u, word_result->NumCharacters());
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(1u, word_result->NumCharacters());
-
-  ASSERT_FALSE(iterator.Next(&word_result));
-}
-
-TEST_F(CachingWordShaperTest, SegmentCJKAndCommon) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-
-  const UChar kStr[] = {'a',    'b',
-                        0xFF08,   // FULLWIDTH LEFT PARENTHESIS (script=common)
-                        0x56FD,   // CJK Unified Ideograph
-                        0x56FD,   // CJK Unified Ideograph
-                        0x56FD,   // CJK Unified Ideograph
-                        0x3002};  // IDEOGRAPHIC FULL STOP (script=common)
-  TextRun text_run{base::span(kStr)};
-
-  const ShapeResult* word_result = nullptr;
-  CachingWordShapeIterator iterator(cache.Get(), text_run, font);
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(2u, word_result->NumCharacters());
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(2u, word_result->NumCharacters());
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(1u, word_result->NumCharacters());
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(2u, word_result->NumCharacters());
-
-  ASSERT_FALSE(iterator.Next(&word_result));
-}
-
-TEST_F(CachingWordShaperTest, SegmentCJKAndInherit) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-
-  const UChar kStr[] = {
-      0x304B,   // HIRAGANA LETTER KA
-      0x304B,   // HIRAGANA LETTER KA
-      0x3009,   // COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK
-      0x304B};  // HIRAGANA LETTER KA
-  TextRun text_run{base::span(kStr)};
-
-  const ShapeResult* word_result = nullptr;
-  CachingWordShapeIterator iterator(cache.Get(), text_run, font);
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(1u, word_result->NumCharacters());
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(2u, word_result->NumCharacters());
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(1u, word_result->NumCharacters());
-
-  ASSERT_FALSE(iterator.Next(&word_result));
-}
-
-TEST_F(CachingWordShaperTest, SegmentCJKAndNonCJKCommon) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-
-  const UChar kStr[] = {0x56FD,  // CJK Unified Ideograph
-                        ' '};
-  TextRun text_run{base::span(kStr)};
-
-  const ShapeResult* word_result = nullptr;
-  CachingWordShapeIterator iterator(cache.Get(), text_run, font);
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(1u, word_result->NumCharacters());
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(1u, word_result->NumCharacters());
-
-  ASSERT_FALSE(iterator.Next(&word_result));
-}
-
-TEST_F(CachingWordShaperTest, SegmentEmojiSequences) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-
-  std::vector<std::string> test_strings = {
-      // A family followed by a couple with heart emoji sequence,
-      // the latter including a variation selector.
-      "\U0001f468\u200D\U0001f469\u200D\U0001f467\u200D\U0001f466\U0001f469"
-      "\u200D\u2764\uFE0F\u200D\U0001f48b\u200D\U0001f468",
-      // Pirate flag
-      "\U0001F3F4\u200D\u2620\uFE0F",
-      // Pilot, judge sequence
-      "\U0001f468\U0001f3fb\u200D\u2696\uFE0F\U0001f468\U0001f3fb\u200D\u2708"
-      "\uFE0F",
-      // Woman, Kiss, Man sequence
-      "\U0001f469\u200D\u2764\uFE0F\u200D\U0001f48b\u200D\U0001f468",
-      // Signs of horns with skin tone modifier
-      "\U0001f918\U0001f3fb",
-      // Man, dark skin tone, red hair
-      "\U0001f468\U0001f3ff\u200D\U0001f9b0"};
-
-  for (auto test_string : test_strings) {
-    String emoji_string = String::FromUTF8(test_string);
-    TextRun text_run(emoji_string);
-    const ShapeResult* word_result = nullptr;
-    CachingWordShapeIterator iterator(cache.Get(), text_run, font);
-
-    ASSERT_TRUE(iterator.Next(&word_result));
-    EXPECT_EQ(emoji_string.length(), word_result->NumCharacters())
-        << " Length mismatch for sequence: " << test_string;
-
-    ASSERT_FALSE(iterator.Next(&word_result));
-  }
-}
-
-TEST_F(CachingWordShaperTest, SegmentEmojiExtraZWJPrefix) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-
-  // A ZWJ, followed by a family and a heart-kiss sequence.
-  const UChar kStr[] = {0x200D, 0xD83D, 0xDC68, 0x200D, 0xD83D, 0xDC69,
-                        0x200D, 0xD83D, 0xDC67, 0x200D, 0xD83D, 0xDC66,
-                        0xD83D, 0xDC69, 0x200D, 0x2764, 0xFE0F, 0x200D,
-                        0xD83D, 0xDC8B, 0x200D, 0xD83D, 0xDC68};
-  TextRun text_run{base::span(kStr)};
-
-  const ShapeResult* word_result = nullptr;
-  CachingWordShapeIterator iterator(cache.Get(), text_run, font);
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(1u, word_result->NumCharacters());
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(22u, word_result->NumCharacters());
-
-  ASSERT_FALSE(iterator.Next(&word_result));
-}
-
-TEST_F(CachingWordShaperTest, SegmentEmojiSubdivisionFlags) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-
-  // Subdivision flags for Wales, Scotland, England.
-  const UChar kStr[] = {0xD83C, 0xDFF4, 0xDB40, 0xDC67, 0xDB40, 0xDC62, 0xDB40,
-                        0xDC77, 0xDB40, 0xDC6C, 0xDB40, 0xDC73, 0xDB40, 0xDC7F,
-                        0xD83C, 0xDFF4, 0xDB40, 0xDC67, 0xDB40, 0xDC62, 0xDB40,
-                        0xDC73, 0xDB40, 0xDC63, 0xDB40, 0xDC74, 0xDB40, 0xDC7F,
-                        0xD83C, 0xDFF4, 0xDB40, 0xDC67, 0xDB40, 0xDC62, 0xDB40,
-                        0xDC65, 0xDB40, 0xDC6E, 0xDB40, 0xDC67, 0xDB40, 0xDC7F};
-  TextRun text_run{base::span(kStr)};
-
-  const ShapeResult* word_result = nullptr;
-  CachingWordShapeIterator iterator(cache.Get(), text_run, font);
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(42u, word_result->NumCharacters());
-
-  ASSERT_FALSE(iterator.Next(&word_result));
-}
-
-TEST_F(CachingWordShaperTest, SegmentCJKCommon) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-
-  const UChar kStr[] = {0xFF08,   // FULLWIDTH LEFT PARENTHESIS (script=common)
-                        0xFF08,   // FULLWIDTH LEFT PARENTHESIS (script=common)
-                        0xFF08};  // FULLWIDTH LEFT PARENTHESIS (script=common)
-  TextRun text_run{base::span(kStr)};
-
-  const ShapeResult* word_result = nullptr;
-  CachingWordShapeIterator iterator(cache.Get(), text_run, font);
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(3u, word_result->NumCharacters());
-
-  ASSERT_FALSE(iterator.Next(&word_result));
-}
-
-TEST_F(CachingWordShaperTest, SegmentCJKCommonAndNonCJK) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-
-  const UChar kStr[] = {0xFF08,  // FULLWIDTH LEFT PARENTHESIS (script=common)
-                        'a', 'b'};
-  TextRun text_run{base::span(kStr)};
-
-  const ShapeResult* word_result = nullptr;
-  CachingWordShapeIterator iterator(cache.Get(), text_run, font);
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(1u, word_result->NumCharacters());
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(2u, word_result->NumCharacters());
-
-  ASSERT_FALSE(iterator.Next(&word_result));
-}
-
-TEST_F(CachingWordShaperTest, SegmentCJKSmallFormVariants) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-
-  const UChar kStr[] = {0x5916,   // CJK UNIFIED IDEOGRPAH
-                        0xFE50};  // SMALL COMMA
-  TextRun text_run{base::span(kStr)};
-
-  const ShapeResult* word_result = nullptr;
-  CachingWordShapeIterator iterator(cache.Get(), text_run, font);
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(2u, word_result->NumCharacters());
-
-  ASSERT_FALSE(iterator.Next(&word_result));
-}
-
-TEST_F(CachingWordShaperTest, SegmentHangulToneMark) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-
-  const UChar kStr[] = {0xC740,   // HANGUL SYLLABLE EUN
-                        0x302E};  // HANGUL SINGLE DOT TONE MARK
-  TextRun text_run{base::span(kStr)};
-
-  const ShapeResult* word_result = nullptr;
-  CachingWordShapeIterator iterator(cache.Get(), text_run, font);
-
-  ASSERT_TRUE(iterator.Next(&word_result));
-  EXPECT_EQ(2u, word_result->NumCharacters());
-
-  ASSERT_FALSE(iterator.Next(&word_result));
-}
-
-TEST_F(CachingWordShaperTest, GlyphBoundsWithSpaces) {
-  Font* font = MakeGarbageCollected<Font>(font_description);
-  CachingWordShaper shaper(*font);
-
-  TextRun periods(base::byte_span_from_cstring(".........."));
-  gfx::RectF periods_glyph_bounds;
-  float periods_width = shaper.Width(periods, &periods_glyph_bounds);
-
-  TextRun periods_and_spaces(
-      base::byte_span_from_cstring(". . . . . . . . . ."));
-  gfx::RectF periods_and_spaces_glyph_bounds;
-  float periods_and_spaces_width =
-      shaper.Width(periods_and_spaces, &periods_and_spaces_glyph_bounds);
-
-  // The total width of periods and spaces should be longer than the width of
-  // periods alone.
-  ASSERT_GT(periods_and_spaces_width, periods_width);
-
-  // The glyph bounds of periods and spaces should be longer than the glyph
-  // bounds of periods alone.
-  ASSERT_GT(periods_and_spaces_glyph_bounds.width(),
-            periods_glyph_bounds.width());
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_fuzzer.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_fuzzer.cc
index bacce514..56c52e9 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_fuzzer.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_fuzzer.cc
@@ -78,10 +78,19 @@
     PlainTextNode* node = MakeGarbageCollected<PlainTextNode>(
         subrun, /* normalize_space */ false, font, /* supports_bidi */ true,
         nullptr);
-    ShapeResultBloberizer::FillGlyphs bloberizer(
-        font.GetFontDescription(), *node,
-        ShapeResultBloberizer::Type::kEmitText);
-    bloberizer.Blobs();
+    if (!node->ContainsRtlItems()) {
+      ShapeResultBloberizer::FillGlyphs bloberizer(
+          font.GetFontDescription(), *node,
+          ShapeResultBloberizer::Type::kEmitText);
+      bloberizer.Blobs();
+    } else {
+      for (const PlainTextItem& item : node->ItemList()) {
+        ShapeResultBloberizer::FillGlyphsNG bloberizer(
+            font.GetFontDescription(), item.Text(), 0, item.Length(),
+            item.EnsureView(), ShapeResultBloberizer::Type::kEmitText);
+        bloberizer.Blobs();
+      }
+    }
   }
 
   return 0;
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
index d55d788..427cff7 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
@@ -2118,13 +2118,9 @@
   }
 }
 
-// Broken on Apple platforms: https://crbug.com/1194323
-#if BUILDFLAG(IS_APPLE)
-#define MAYBE_EmojiPercentage DISABLED_EmojiPercentage
-#else
-#define MAYBE_EmojiPercentage EmojiPercentage
-#endif
-TEST_F(HarfBuzzShaperTest, MAYBE_EmojiPercentage) {
+// As the comment indicate, this test is not valid when Noto Color Emoji from
+// the third_party directory is updated to Unicode 13 or newer.
+TEST_F(HarfBuzzShaperTest, DISABLED_EmojiPercentage) {
 #if BUILDFLAG(IS_WIN)
   if (base::win::OSInfo::GetInstance()->version() >=
       base::win::Version::WIN11) {
diff --git a/third_party/blink/renderer/platform/geometry/path_builder.cc b/third_party/blink/renderer/platform/geometry/path_builder.cc
index 7e3d4690..1d6b8160 100644
--- a/third_party/blink/renderer/platform/geometry/path_builder.cc
+++ b/third_party/blink/renderer/platform/geometry/path_builder.cc
@@ -61,7 +61,7 @@
 // Adds a curved corner to a path. The vertex argument is the 4 points
 // of the corner rectangle, starting from the beginning of the corner
 // and continuing clockwise.
-void AddCurvedCorner(SkPath& path, const Corner& corner) {
+void AddCurvedCorner(SkPathBuilder& path, const Corner& corner) {
   if (corner.IsConcave()) {
     AddCurvedCorner(path, corner.Inverse());
     return;
@@ -106,35 +106,39 @@
 
 PathBuilder::PathBuilder(const Path& path) : builder_(path.GetSkPath()) {}
 
+void PathBuilder::ClearCachedData() {
+  current_path_.reset();
+  current_bounds_.reset();
+}
+
 void PathBuilder::Reset() {
   builder_.reset();
-  current_path_.reset();
+  ClearCachedData();
 }
 
 Path PathBuilder::Finalize() {
-  Path path(std::move(builder_));
-
-  Reset();
-
-  return path;
+  ClearCachedData();
+  return builder_.detach();
 }
 
 gfx::RectF PathBuilder::BoundingRect() const {
-  return gfx::SkRectToRectF(builder_.getBounds());
+  if (!current_bounds_) {
+    current_bounds_.emplace(gfx::SkRectToRectF(builder_.computeBounds()));
+  }
+  return current_bounds_.value();
 }
 
 const Path& PathBuilder::CurrentPath() const {
   if (!current_path_) {
-    current_path_.emplace(builder_);
+    current_path_.emplace(builder_.snapshot());
   }
 
   return current_path_.value();
 }
 
 std::optional<gfx::PointF> PathBuilder::CurrentPoint() const {
-  SkPoint point;
-  if (builder_.getLastPt(&point)) {
-    return gfx::SkPointToPointF(point);
+  if (auto point = builder_.getLastPt()) {
+    return gfx::SkPointToPointF(*point);
   }
   return std::nullopt;
 }
@@ -142,21 +146,21 @@
 PathBuilder& PathBuilder::Close() {
   builder_.close();
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
 PathBuilder& PathBuilder::MoveTo(const gfx::PointF& pt) {
   builder_.moveTo(gfx::PointFToSkPoint(pt));
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
 PathBuilder& PathBuilder::LineTo(const gfx::PointF& pt) {
   builder_.lineTo(gfx::PointFToSkPoint(pt));
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
@@ -164,7 +168,7 @@
                                  const gfx::PointF& pt) {
   builder_.quadTo(gfx::PointFToSkPoint(ctrl), gfx::PointFToSkPoint(pt));
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
@@ -174,7 +178,7 @@
   builder_.cubicTo(gfx::PointFToSkPoint(ctrl1), gfx::PointFToSkPoint(ctrl2),
                    gfx::PointFToSkPoint(pt));
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
@@ -184,12 +188,13 @@
                                 float x_rotate,
                                 bool large_arc,
                                 bool sweep) {
-  builder_.arcTo(radius_x, radius_y, x_rotate,
-                 large_arc ? SkPath::kLarge_ArcSize : SkPath::kSmall_ArcSize,
-                 sweep ? SkPathDirection::kCW : SkPathDirection::kCCW, p.x(),
-                 p.y());
+  builder_.arcTo(
+      SkVector{radius_x, radius_y}, x_rotate,
+      large_arc ? SkPathBuilder::kLarge_ArcSize : SkPathBuilder::kSmall_ArcSize,
+      sweep ? SkPathDirection::kCW : SkPathDirection::kCCW,
+      gfx::PointFToSkPoint(p));
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
@@ -198,7 +203,7 @@
                                 float radius) {
   builder_.arcTo(gfx::PointFToSkPoint(p1), gfx::PointFToSkPoint(p2), radius);
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
@@ -208,7 +213,7 @@
                                     opposite_point.y()),
                    SkPathDirection::kCW, 0);
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
@@ -216,7 +221,7 @@
                                   const AffineTransform& transform) {
   builder_.addPath(src.GetSkPath(), transform.ToSkMatrix());
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
@@ -226,13 +231,14 @@
                     clockwise ? SkPathDirection::kCW : SkPathDirection::kCCW,
                     /* start at upper-left after corner radius */ 0);
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
 PathBuilder& PathBuilder::AddCorner(const ContouredRect::Corner& corner) {
   AddCurvedCorner(builder_, corner);
-  current_path_.reset();
+
+  ClearCachedData();
   return *this;
 }
 
@@ -256,7 +262,7 @@
     AddCurvedCorner(builder_, contoured_rect.BottomLeftCorner());
     AddCurvedCorner(builder_, contoured_rect.TopLeftCorner());
     Close();
-    current_path_.reset();
+    ClearCachedData();
   };
 
   if (origin_rect == target_rect) {
@@ -337,7 +343,7 @@
                      gfx::PointF(origin_rect.Rect().right(), outer_rect.y())));
     }
     Close();
-    current_path_.reset();
+    ClearCachedData();
     return *this;
   }
 
@@ -363,43 +369,43 @@
                                       contoured_rect.GetCornerCurvature());
 
   if (!origin_rect.GetRadii().TopRight().IsEmpty()) {
-    SkPath path;
+    SkPathBuilder path;
     path.moveTo(infinite_rect.left(), infinite_rect.top());
     AddCurvedCorner(path, contoured_rect.TopRightCorner());
     path.lineTo(infinite_rect.right(), infinite_rect.bottom());
     path.lineTo(infinite_rect.left(), infinite_rect.bottom());
     path.close();
-    op_builder.add(path, kIntersect_SkPathOp);
+    op_builder.add(path.detach(), kIntersect_SkPathOp);
   }
 
   if (!origin_rect.GetRadii().BottomRight().IsEmpty()) {
-    SkPath path;
+    SkPathBuilder path;
     path.moveTo(infinite_rect.right(), infinite_rect.top());
     AddCurvedCorner(path, contoured_rect.BottomRightCorner());
     path.lineTo(infinite_rect.left(), infinite_rect.bottom());
     path.lineTo(infinite_rect.left(), infinite_rect.top());
     path.close();
-    op_builder.add(path, kIntersect_SkPathOp);
+    op_builder.add(path.detach(), kIntersect_SkPathOp);
   }
 
   if (!origin_rect.GetRadii().BottomLeft().IsEmpty()) {
-    SkPath path;
+    SkPathBuilder path;
     path.moveTo(infinite_rect.right(), infinite_rect.bottom());
     AddCurvedCorner(path, contoured_rect.BottomLeftCorner());
     path.lineTo(infinite_rect.left(), infinite_rect.top());
     path.lineTo(infinite_rect.right(), infinite_rect.top());
     path.close();
-    op_builder.add(path, kIntersect_SkPathOp);
+    op_builder.add(path.detach(), kIntersect_SkPathOp);
   }
 
   if (!origin_rect.GetRadii().TopLeft().IsEmpty()) {
-    SkPath path;
+    SkPathBuilder path;
     path.moveTo(infinite_rect.left(), infinite_rect.bottom());
     AddCurvedCorner(path, contoured_rect.TopLeftCorner());
     path.lineTo(infinite_rect.right(), infinite_rect.top());
     path.lineTo(infinite_rect.right(), infinite_rect.bottom());
     path.close();
-    op_builder.add(path, kIntersect_SkPathOp);
+    op_builder.add(path.detach(), kIntersect_SkPathOp);
   }
 
   SkPath result;
@@ -408,7 +414,7 @@
   } else {
     DrawAsSinglePath();
   }
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
@@ -445,7 +451,7 @@
   } else {
     builder_.arcTo(oval, start_degrees, sweep_degrees, false);
   }
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
@@ -477,7 +483,7 @@
   // Start at upper-left, add clock-wise.
   builder_.addRect(gfx::RectFToSkRect(rect), SkPathDirection::kCW, 0);
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
@@ -490,34 +496,34 @@
                        center.x() + radius_x, center.y() + radius_y),
       SkPathDirection::kCW, 1);
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
 PathBuilder& PathBuilder::SetWindRule(WindRule rule) {
   const SkPathFillType fill_type = WebCoreWindRuleToSkFillType(rule);
 
-  if (fill_type == builder_.getFillType()) {
+  if (fill_type == builder_.fillType()) {
     return *this;
   }
 
   builder_.setFillType(fill_type);
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
 PathBuilder& PathBuilder::Translate(const gfx::Vector2dF& offset) {
   builder_.offset(offset.x(), offset.y());
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
 PathBuilder& PathBuilder::Transform(const AffineTransform& xform) {
   builder_.transform(xform.ToSkMatrix());
 
-  current_path_.reset();
+  ClearCachedData();
   return *this;
 }
 
diff --git a/third_party/blink/renderer/platform/geometry/path_builder.h b/third_party/blink/renderer/platform/geometry/path_builder.h
index 77d1a96..d329a8d 100644
--- a/third_party/blink/renderer/platform/geometry/path_builder.h
+++ b/third_party/blink/renderer/platform/geometry/path_builder.h
@@ -12,7 +12,7 @@
 #include "third_party/blink/renderer/platform/geometry/path_types.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-#include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkPathBuilder.h"
 
 namespace gfx {
 
@@ -133,10 +133,12 @@
   PathBuilder& Transform(const AffineTransform&);
 
  private:
-  // TODO(crbug.com/378688986): switch to SkPathBuilder when ready.
-  SkPath builder_;
+  void ClearCachedData();
+
+  SkPathBuilder builder_;
 
   mutable std::optional<Path> current_path_;
+  mutable std::optional<gfx::RectF> current_bounds_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index 921fe5c6..b1ab1658 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -70,12 +70,6 @@
              base::FEATURE_DISABLED_BY_DEFAULT
 #endif
 );
-
-// Controls whether CanvasResource::WaitSyncToken(const SyncToken&) should
-// defer wait (when enabled) or wait immediately (when disabled).
-BASE_FEATURE(kCanvasResourceDefersWaitSyncToken,
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 }  // namespace
 
 CanvasResource::CanvasResource()
@@ -471,7 +465,6 @@
   // complete.
   WaitSyncToken(external_write_sync_token);
 
-  WaitSyncToken();
   // Additionally ensure that the next compositor read waits for the external
   // write to complete by ensuring that a new sync token is generated on the
   // internal interface as part of generating the TransferableResource. This new
@@ -515,9 +508,7 @@
     const gpu::SyncToken& sync_token) {
   if (sync_token.HasData()) {
     acquire_sync_token_ = sync_token;
-    if (!base::FeatureList::IsEnabled(kCanvasResourceDefersWaitSyncToken)) {
-      WaitSyncToken();
-    }
+    WaitSyncToken();
   }
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 7239e3c..2ba8bba 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -1727,6 +1727,15 @@
   client_->DrawingBufferClientRestorePixelPackBufferBinding();
 }
 
+bool DrawingBuffer::SupportsConcurrentReadWrite() {
+  if (!back_color_buffer_) {
+    return false;
+  }
+
+  return back_color_buffer_->shared_image->usage().Has(
+      gpu::SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE);
+}
+
 bool DrawingBuffer::Multisample() const {
   return anti_aliasing_mode_ != kAntialiasingModeNone;
 }
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
index c6f9294f..84c0116 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -312,6 +312,10 @@
 
   bool UsingSwapChain() const { return using_swap_chain_; }
 
+  // Returns true if the drawing buffer supports being concurrently written to
+  // and read from.
+  bool SupportsConcurrentReadWrite();
+
   // Keep track of low latency buffer status.
   bool low_latency_enabled() const { return low_latency_enabled_; }
   void set_low_latency_enabled(bool low_latency_enabled) {
diff --git a/third_party/blink/renderer/platform/graphics/mailbox_texture_backing.h b/third_party/blink/renderer/platform/graphics/mailbox_texture_backing.h
index 77f6f8c..ab66c937 100644
--- a/third_party/blink/renderer/platform/graphics/mailbox_texture_backing.h
+++ b/third_party/blink/renderer/platform/graphics/mailbox_texture_backing.h
@@ -47,7 +47,6 @@
                   size_t dst_row_bytes,
                   int src_x,
                   int src_y) override;
-  void FlushPendingSkiaOps() override {}
 
  private:
   const sk_sp<SkImage> sk_image_;
diff --git a/third_party/blink/renderer/platform/loader/fetch/DEPS b/third_party/blink/renderer/platform/loader/fetch/DEPS
index ec8c35f0..1f718b2 100644
--- a/third_party/blink/renderer/platform/loader/fetch/DEPS
+++ b/third_party/blink/renderer/platform/loader/fetch/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+base/containers/flat_set.h",
+  "+base/memory_coordinator",
   "+components/subresource_filter/core/common/scoped_rule.h",
   "+net/base/auth.h",
   "+net/base/ip_endpoint.h",
diff --git a/third_party/blink/renderer/platform/loader/fetch/console_logger.h b/third_party/blink/renderer/platform/loader/fetch/console_logger.h
index 2f11b21..2c9796d 100644
--- a/third_party/blink/renderer/platform/loader/fetch/console_logger.h
+++ b/third_party/blink/renderer/platform/loader/fetch/console_logger.h
@@ -28,6 +28,12 @@
   ConsoleLogger() = default;
   virtual ~ConsoleLogger() = default;
 
+  // Please familiarize yourself with http://goo.gle/devtools-console-policy
+  // prior to adding new console messages, and make sure that you understand the
+  // implications on the developer experience. A good console message should be
+  // actionable and relevant to what the developer is currently doing. Using the
+  // DevTools Console panel as a means to advertise best practices or Chromium
+  // agendas has shown to be counterproductive.
   void AddConsoleMessage(mojom::blink::ConsoleMessageSource source,
                          mojom::blink::ConsoleMessageLevel level,
                          const String& message,
@@ -37,6 +43,12 @@
     AddConsoleMessageImpl(source, level, message, discard_duplicates, category);
   }
 
+  // Please familiarize yourself with http://goo.gle/devtools-console-policy
+  // prior to adding new console messages, and make sure that you understand the
+  // implications on the developer experience. A good console message should be
+  // actionable and relevant to what the developer is currently doing. Using the
+  // DevTools Console panel as a means to advertise best practices or Chromium
+  // agendas has shown to be counterproductive.
   void AddConsoleMessage(ConsoleMessage* message,
                          bool discard_duplicates = false) {
     AddConsoleMessageImpl(message, discard_duplicates);
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc b/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc
index 0530caa..bc3c7dba1 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc
@@ -28,11 +28,14 @@
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
+#include "base/memory_coordinator/memory_consumer_registry.h"
+#include "base/memory_coordinator/traits.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
 #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
@@ -46,6 +49,28 @@
 namespace blink {
 
 namespace {
+
+// The set of traits that describes the behavior of MemoryCache.
+constexpr base::MemoryConsumerTraits kMemoryCacheTraits = {
+    .supports_memory_limit =
+        base::MemoryConsumerTraits::SupportsMemoryLimit::kYes,
+    .in_process = base::MemoryConsumerTraits::InProcess::kYes,
+    .estimated_memory_usage =
+        base::MemoryConsumerTraits::EstimatedMemoryUsage::kMedium,
+    .release_memory_cost =
+        base::MemoryConsumerTraits::ReleaseMemoryCost::kRequiresTraversal,
+    .recreate_memory_cost = base::MemoryConsumerTraits::RecreateMemoryCost::kNA,
+    .information_retention =
+        base::MemoryConsumerTraits::InformationRetention::kLossless,
+    .memory_release_behavior =
+        base::MemoryConsumerTraits::MemoryReleaseBehavior::kIdempotent,
+    .execution_type = base::MemoryConsumerTraits::ExecutionType::kAsynchronous,
+    .release_gc_references =
+        base::MemoryConsumerTraits::ReleaseGCReferences::kYes,
+    .garbage_collects_v8_heap =
+        base::MemoryConsumerTraits::GarbageCollectsV8Heap::kNo,
+};
+
 // Use function-local statics to cache the feature parameters. This avoids
 // global constructors and ensures the .Get() call happens only once.
 double GetFrequencyWeight() {
@@ -135,12 +160,24 @@
 static constexpr char kPageSavedResourceStrongReferenceSize[] =
     "Blink.MemoryCache.PageSavedResourceStrongReferenceSize2";
 
-MemoryCache* ReplaceMemoryCacheForTesting(MemoryCache* cache) {
-  MemoryCache::Get();
-  MemoryCache* old_cache = g_memory_cache->Release();
-  *g_memory_cache = cache;
-  MemoryCacheDumpProvider::Instance()->SetMemoryCache(cache);
-  return old_cache;
+ScopedMemoryCacheForTesting::ScopedMemoryCacheForTesting(
+    Persistent<MemoryCache> cache) {
+  if (!g_memory_cache) {
+    g_memory_cache = new Persistent<MemoryCache>(std::move(cache));
+    return;
+  }
+
+  stored_cache_ = std::exchange(*g_memory_cache, std::move(cache));
+}
+
+ScopedMemoryCacheForTesting::~ScopedMemoryCacheForTesting() {
+  if (stored_cache_) {
+    *g_memory_cache = std::move(stored_cache_);
+  } else {
+    delete g_memory_cache;
+    g_memory_cache = nullptr;
+  }
+  blink::ThreadState::Current()->CollectAllGarbageForTesting();
 }
 
 void MemoryCacheEntry::Trace(Visitor* visitor) const {
@@ -168,7 +205,18 @@
 
 MemoryCache::MemoryCache(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : strong_references_prune_duration_(
+    : scoped_memory_consumer_registration_(
+          (base::SingleThreadTaskRunner::GetMainThreadDefault()
+               ->RunsTasksInCurrentSequence() &&
+           base::MemoryConsumerRegistry::Exists())
+              ? std::make_unique<base::ScopedMemoryConsumerRegistration>(
+                    "MemoryCache",
+                    kMemoryCacheTraits,
+                    this)
+              : nullptr),
+      strong_references_max_size_(
+          features::kMemoryCacheStrongReferenceTotalSizeThresholdParam.Get()),
+      strong_references_prune_duration_(
           kMemoryCacheStrongReferencePruneDelay.Get()),
       task_runner_(std::move(task_runner)) {
   MemoryCacheDumpProvider::Instance()->SetMemoryCache(this);
@@ -512,6 +560,19 @@
   }
 }
 
+void MemoryCache::OnReleaseMemory() {
+  PruneStrongReferences();
+}
+
+void MemoryCache::OnUpdateMemoryLimit() {
+  // It is important to not do any memory management in this function. The max
+  // size is updated to the requested limit without calling
+  // PruneStrongReferences().
+  strong_references_max_size_ =
+      features::kMemoryCacheStrongReferenceTotalSizeThresholdParam.Get() *
+      memory_limit_ratio();
+}
+
 void MemoryCache::SaveTieredStrongReference(Resource* resource) {
   if (tiered_strong_references_.Contains(resource)) {
     return;
@@ -549,8 +610,7 @@
   // the O(N log N) sorting step is not a bottleneck in production.
   SCOPED_UMA_HISTOGRAM_TIMER("MemoryCache.PruneTieredStrongReferences.Time");
 
-  const size_t max_threshold = static_cast<size_t>(
-      features::kMemoryCacheStrongReferenceTotalSizeThresholdParam.Get());
+  const size_t max_threshold = strong_references_max_size_;
 
   // Enforce a maximum lifetime for all strong references.
   const base::TimeTicks now = base::TimeTicks::Now();
@@ -609,8 +669,7 @@
   SCOPED_UMA_HISTOGRAM_TIMER("MemoryCache.PruneStrongReferences.Time");
 
   DCHECK(base::FeatureList::IsEnabled(features::kMemoryCacheStrongReference));
-  static const size_t max_threshold = static_cast<size_t>(
-      features::kMemoryCacheStrongReferenceTotalSizeThresholdParam.Get());
+  const size_t max_threshold = strong_references_max_size_;
 
   base::TimeTicks last_ticks;
   size_t strong_reference_total_size = 0;
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache.h b/third_party/blink/renderer/platform/loader/fetch/memory_cache.h
index ab7243a..980a41b 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache.h
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache.h
@@ -27,6 +27,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_MEMORY_CACHE_H_
 
 #include "base/gtest_prod_util.h"
+#include "base/memory_coordinator/memory_consumer.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
@@ -70,7 +71,8 @@
 // stylesheets, etc.
 class PLATFORM_EXPORT MemoryCache final : public GarbageCollected<MemoryCache>,
                                           public MemoryCacheDumpClient,
-                                          public MemoryPressureListener {
+                                          public MemoryPressureListener,
+                                          public base::MemoryConsumer {
  public:
   explicit MemoryCache(scoped_refptr<base::SingleThreadTaskRunner> task_runner);
   MemoryCache(const MemoryCache&) = delete;
@@ -167,6 +169,10 @@
   void OnMemoryPressure(
       base::MemoryPressureListener::MemoryPressureLevel) override;
 
+  // base::MemoryConsumer:
+  void OnReleaseMemory() override;
+  void OnUpdateMemoryLimit() override;
+
  private:
   // A URL-based map of all resources that are in the cache (including the
   // freshest version of objects that are currently being referenced by a Web
@@ -206,9 +212,17 @@
 
   double CalculateResourceValue(const Resource* resource) const;
 
+  std::unique_ptr<base::ScopedMemoryConsumerRegistration>
+      scoped_memory_consumer_registration_;
+
   // The number of bytes currently consumed by resources in the cache.
   size_t size_ = 0;
 
+  // The maximum size of `strong_references_` or `tiered_strong_references_`.
+  // This limit decreases or increases when notified by the MemoryConsumer
+  // interface.
+  size_t strong_references_max_size_;
+
   // An LRU linked list. The tail contains the most recent items. When
   // an item is accessed via `ResourceAccessed` it is moved to the end
   // of the list. This list is pruned from the front based on size and
@@ -227,11 +241,20 @@
   FRIEND_TEST_ALL_PREFIXES(MemoryCacheStrongReferenceTest, LRU);
   FRIEND_TEST_ALL_PREFIXES(MemoryCacheStrongReferenceTest,
                            ClearStrongReferences);
+  FRIEND_TEST_ALL_PREFIXES(MemoryCacheStrongReferenceTest,
+                           ChangeMemoryCacheSize);
 };
 
-// Sets the global cache, used to swap in a test instance. Returns the old
-// MemoryCache object.
-PLATFORM_EXPORT MemoryCache* ReplaceMemoryCacheForTesting(MemoryCache*);
+// Sets the global cache, used to swap in a test instance. Saves the old
+// MemoryCache object and restores it in the destructor..
+class PLATFORM_EXPORT ScopedMemoryCacheForTesting {
+ public:
+  explicit ScopedMemoryCacheForTesting(Persistent<MemoryCache>);
+  ~ScopedMemoryCacheForTesting();
+
+ private:
+  Persistent<MemoryCache> stored_cache_;
+};
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc b/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
index 512cf254..85248dff 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
@@ -64,6 +64,10 @@
 
 class MemoryCacheCorrectnessTest : public testing::Test {
  protected:
+  MemoryCacheCorrectnessTest()
+      : scoped_memory_cache_(
+            MakeGarbageCollected<MemoryCache>(platform_->test_task_runner())) {}
+
   MockResource* ResourceFromResourceResponse(ResourceResponse response) {
     if (response.CurrentRequestUrl().IsNull())
       response.SetCurrentRequestUrl(KURL(kResourceURL));
@@ -118,10 +122,6 @@
  private:
   // Overrides testing::Test.
   void SetUp() override {
-    // Save the global memory cache to restore it upon teardown.
-    global_memory_cache_ = ReplaceMemoryCacheForTesting(
-        MakeGarbageCollected<MemoryCache>(platform_->test_task_runner()));
-
     security_origin_ = SecurityOrigin::CreateUniqueOpaque();
     MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
     auto* properties =
@@ -137,20 +137,15 @@
     Resource::SetClockForTesting(platform_->test_task_runner()->GetMockClock());
   }
   void TearDown() override {
-    MemoryCache::Get()->EvictResources();
-
     Resource::SetClockForTesting(nullptr);
-
-    // Yield the ownership of the global memory cache back.
-    ReplaceMemoryCacheForTesting(global_memory_cache_.Release());
   }
 
-  base::test::SingleThreadTaskEnvironment task_environment_;
-  Persistent<MemoryCache> global_memory_cache_;
+  base::test::TaskEnvironment task_environment_;
   scoped_refptr<const SecurityOrigin> security_origin_;
   Persistent<ResourceFetcher> fetcher_;
   ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
       platform_;
+  ScopedMemoryCacheForTesting scoped_memory_cache_;
 };
 
 TEST_F(MemoryCacheCorrectnessTest, FreshFromLastModified) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc b/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
index ecf0a8f..13109b0 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
@@ -33,6 +33,7 @@
 #include <string_view>
 #include <variant>
 
+#include "base/memory_coordinator/test_memory_consumer_registry.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -112,11 +113,12 @@
     void DestroyDecodedDataIfPossible() override { SetDecodedSize(0u); }
   };
 
+  MemoryCacheTest()
+      : scoped_memory_cache_(
+            MakeGarbageCollected<MemoryCache>(platform_->test_task_runner())) {}
+
  protected:
   void SetUp() override {
-    // Save the global memory cache to restore it upon teardown.
-    global_memory_cache_ = ReplaceMemoryCacheForTesting(
-        MakeGarbageCollected<MemoryCache>(platform_->test_task_runner()));
     auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
     lifecycle_notifier_ = MakeGarbageCollected<MockContextLifecycleNotifier>();
     fetcher_ = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
@@ -127,18 +129,15 @@
         nullptr /* back_forward_cache_loader_helper */));
   }
 
-  void TearDown() override {
-    ReplaceMemoryCacheForTesting(global_memory_cache_.Release());
-  }
-
-  Persistent<MemoryCache> global_memory_cache_;
+  base::test::TaskEnvironment task_environment_;
+  base::TestMemoryConsumerRegistry test_memory_consumer_registry_;
   Persistent<ResourceFetcher> fetcher_;
   Persistent<MockContextLifecycleNotifier> lifecycle_notifier_;
   ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
       platform_;
+  ScopedMemoryCacheForTesting scoped_memory_cache_;
 
  private:
-  base::test::TaskEnvironment task_environment_;
 };
 
 
@@ -394,4 +393,30 @@
   EXPECT_EQ(MemoryCache::Get()->strong_references_.size(), 0u);
 }
 
+TEST_F(MemoryCacheStrongReferenceTest, ChangeMemoryCacheSize) {
+  // Memory cache has a non-null max size, but is empty.
+  EXPECT_NE(MemoryCache::Get()->strong_references_max_size_, 0u);
+  EXPECT_EQ(MemoryCache::Get()->strong_references_.size(), 0u);
+
+  // Add a resource.
+  const KURL kURL("http://test/resource1");
+  Member<FakeResource> resource =
+      MakeGarbageCollected<FakeResource>(kURL, ResourceType::kRaw);
+  MemoryCache::Get()->SaveStrongReference(resource);
+
+  EXPECT_NE(MemoryCache::Get()->strong_references_max_size_, 0u);
+  EXPECT_EQ(MemoryCache::Get()->strong_references_.size(), 1u);
+
+  // Change the memory limit. This will reduce the max size to zero, but not
+  // clear anything yet.
+  test_memory_consumer_registry_.NotifyUpdateMemoryLimit(0);
+  EXPECT_EQ(MemoryCache::Get()->strong_references_max_size_, 0u);
+  EXPECT_EQ(MemoryCache::Get()->strong_references_.size(), 1u);
+
+  // ReleaseMemory notification. This actually calls PruneStrongReferences();
+  test_memory_consumer_registry_.NotifyReleaseMemory();
+  EXPECT_EQ(MemoryCache::Get()->strong_references_max_size_, 0u);
+  EXPECT_EQ(MemoryCache::Get()->strong_references_.size(), 0u);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.cc b/third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.cc
index 7b01829..b2a5f2f 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.cc
@@ -6,8 +6,6 @@
 
 #include "third_party/webrtc/api/candidate.h"
 #include "third_party/webrtc/p2p/base/p2p_constants.h"
-#include "third_party/webrtc/p2p/base/port.h"
-#include "third_party/webrtc/pc/webrtc_sdp.h"
 
 namespace blink {
 
@@ -68,9 +66,13 @@
 }
 
 void RTCIceCandidatePlatform::PopulateFields(bool use_username_from_candidate) {
-  webrtc::Candidate c;
-  if (!webrtc::ParseCandidate(candidate_.Utf8(), &c, nullptr, true))
+  webrtc::RTCErrorOr<webrtc::Candidate> parsed_candidate =
+      webrtc::Candidate::ParseCandidateString(candidate_.Utf8());
+  if (!parsed_candidate.ok()) {
     return;
+  }
+
+  const webrtc::Candidate& c = parsed_candidate.value();
 
   foundation_ = String::FromUTF8(c.foundation());
   component_ = CandidateComponentToString(c.component());
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 121d340..52a6792 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -894,6 +894,12 @@
       status: "stable",
     },
     {
+      // Canvas2D API for specifying HDR tone mapping behavior.
+      // https:/crbug.com/448552449, https://github.com/whatwg/html/issues/11424
+      name: "CanvasToneMapping",
+      status: "experimental",
+    },
+    {
       // Kill switch for https://crbug.com/330506337.
       name: "CanvasUsesArcPaintOp",
       status: "stable",
@@ -1350,6 +1356,12 @@
       status: "experimental",
     },
     {
+      // https://chromestatus.com/feature/5157805733183488
+      name: "CSSGridGapSuppression",
+      status: "experimental",
+      depends_on: ["CSSGapDecoration"],
+    },
+    {
       // This needs to be kept as a runtime flag as long as we need to forcibly
       // disable it for WebView on Android versions older than P. See
       // https://crrev.com/f311a84728272e30979432e8474089b3db3c67df
@@ -1682,13 +1694,6 @@
       status: "stable",
     },
     {
-      // Replaces \r\n and \r with \n in setCustomValidity.
-      // https://github.com/whatwg/html/pull/10350
-      // https://issues.chromium.org/issues/340814283
-      name: "CustomValidityNormalizeNewlines",
-      status: "stable",
-    },
-    {
       // https://chromestatus.com/feature/5134293578285056
       name: "Database",
     },
@@ -1789,13 +1794,6 @@
       status: "experimental",
     },
     {
-      // Prevents <form method=dialog> from submitting across shadow boundaries
-      // when looking for an ancestor <dialog> element.
-      // https://issues.chromium.org/issues/40229445
-      name: "DialogSubmitShadowBoundaries",
-      status: "stable",
-    },
-    {
       name: "DigitalGoods",
       origin_trial_feature_name: "DigitalGoodsV2",
       origin_trial_os: ["android", "chromeos"],
@@ -2668,12 +2666,6 @@
       status: "stable",
       base_feature: "none",
     },
-    {
-      // Adds color:gray to the <hr> element in the UA stylesheet.
-      // https://issues.chromium.org/issues/425113244
-      name: "HrElementGray",
-      status: "stable",
-    },
     // The `anchor` attribute, supported by both anchor positioning and the
     // popover API.
     {
@@ -2843,13 +2835,9 @@
       status: "stable",
     },
     {
-      name: "InsertLineBreakIfInlineListItem",
-      status: "stable",
-    },
-    {
       // crbug.com/1420675
       name: "InsertLineBreakIfPhrasingContent",
-      status: {"Android": "test", "default": "stable"},
+      status: "stable",
     },
     {
       // Improved support for debugging CSSNestedDeclarations.
@@ -3340,12 +3328,6 @@
       name: "NoIdleEncodingForWebTests",
       status: "test",
     },
-    // Doesn't increase the end offset on getting the range for a new line
-    // character. See https://crbug.com/326888905
-    {
-      name: "NoIncreasingEndOffsetOnSplittingTextNodes",
-      status: "stable",
-    },
     // Doesn't insert empty blockquotes on outdenting a blockquote. See
     // https://crbug.com/323960902
     {
@@ -3415,15 +3397,6 @@
       status: "stable",
     },
     {
-      // This flag makes the <option> element's label attribute render the
-      // contents of the label attribute even if it is only whitespace in order
-      // to match the spec and firefox. It also stops stripping/simplifying
-      // whitespace when using the label attribute for rendering.
-      // https://github.com/whatwg/html/issues/10955
-      name: "OptionLabelAttributeWhitespace",
-      status: "stable",
-    },
-    {
       name: "OrientationEvent",
       status: {"Android": "stable"},
     },
@@ -4619,10 +4592,6 @@
       status: "experimental",
     },
     {
-      name: "SkipLineBreakItemWhenIsCollapsed",
-      status: "stable",
-    },
-    {
       name: "SkipOofItemForBreakCandidate",
       status: "stable",
     },
@@ -4977,11 +4946,6 @@
       status: "experimental",
     },
     {
-      // crbug.com/325517313
-      name: "TextDiffSplitFix",
-      status: "stable",
-    },
-    {
       // crbug.com/40812040
       name: "TextEmphasisLetterSpacing",
       status: "stable",
@@ -5057,15 +5021,6 @@
       status: "experimental",
     },
     {
-      // Adds an "source" attribute to ToggleEvent which is set to the button
-      // that invoked the element which the toggle event was fired on, if
-      // appropriate.
-      // https://issues.chromium.org/issues/408018828
-      // https://github.com/whatwg/html/issues/9111
-      name: "ToggleEventSource",
-      status: "stable",
-    },
-    {
       name: "TopicsAPI",
       base_feature: "none",
       public: true,
@@ -5853,7 +5808,8 @@
     },
     {
       name: "WidthAndHeightAsPresentationAttributesOnNestedSvg",
-      status: "stable",
+      // Disabled due to web compat issues, see https://crbug.com/449170647.
+      status: "experimental",
     },
     {
       name: "WidthAndHeightStylePropertiesOnUseAndSymbol",
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
index 7f0e726b..92e9334 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -55,7 +55,7 @@
 // etc. after the renderer has been backgrounded. This is used only if
 // background suspension is enabled.
 constexpr base::TimeDelta kDefaultDelayForBackgroundTabFreezing =
-    base::Minutes(5);
+    base::Minutes(1);
 
 // Duration of a throttled wake up.
 constexpr base::TimeDelta kThrottledWakeUpDuration = base::Milliseconds(3);
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
index f4072ba9..73102dda8 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
@@ -1226,6 +1226,10 @@
 }
 
 TEST_F(PageSchedulerImplTest, OpenWebSocketExemptsFromBudgetThrottling) {
+  // Disabling StopInBackground (Android only) makes this easier to test as
+  // WebSockets are not exempt from background freezing.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(blink::features::kStopInBackground);
   InitializeTrialParams();
   std::unique_ptr<PageSchedulerImpl> page_scheduler =
       CreatePageScheduler(nullptr, scheduler_.get(), *agent_group_scheduler_);
diff --git a/third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.cc b/third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.cc
index 4e01fc9ca2..3aea33ce 100644
--- a/third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.cc
+++ b/third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.cc
@@ -150,14 +150,141 @@
 
 void CompositorThreadEventQueue::Queue(
     std::unique_ptr<EventWithCallback> new_event) {
-  if (new_event->first_original_event()) {
-    // Trace could be nested as there might be multiple events in queue.
-    // e.g. |ScrollUpdate|, |ScrollEnd|, and another scroll sequence.
-    TRACE_EVENT_BEGIN(
-        "input", "CompositorThreadEventQueue::Queue",
-        perfetto::Track::FromPointer(new_event->first_original_event()));
+  if (base::FeatureList::IsEnabled(
+          features::kRefactorCompositorThreadEventQueue)) {
+    if (new_event->first_original_event()) {
+      // Trace could be nested as there might be multiple events in queue.
+      // e.g. |ScrollUpdate|, |ScrollEnd|, and another scroll sequence.
+      TRACE_EVENT_BEGIN(
+          "input", "CompositorThreadEventQueue::Queue",
+          perfetto::Track::FromPointer(new_event->first_original_event()));
+    }
+    queue_.push_back(std::move(new_event));
+  } else {
+    if (queue_.empty() ||
+        !IsContinuousGestureEvent(new_event->event().GetType()) ||
+        !(queue_.back()->CanCoalesceWith(*new_event) ||
+          WebGestureEvent::IsCompatibleScrollorPinch(
+              ToWebGestureEvent(new_event->event()),
+              ToWebGestureEvent(queue_.back()->event())))) {
+      if (new_event->first_original_event()) {
+        // Trace could be nested as there might be multiple events in queue.
+        // e.g. |ScrollUpdate|, |ScrollEnd|, and another scroll sequence.
+        TRACE_EVENT_BEGIN(
+            "input", "CompositorThreadEventQueue::Queue",
+            perfetto::Track::FromPointer(new_event->first_original_event()));
+      }
+      queue_.push_back(std::move(new_event));
+      return;
+    }
+
+    if (queue_.back()->CanCoalesceWith(*new_event)) {
+      queue_.back()->CoalesceWith(new_event.get());
+      return;
+    }
+
+    // We have only scrolls or pinches at this point (all other events are
+    // filtered out by the if statements above). We want to coalesce this event
+    // into the previous event(s) and represent it as a scroll and then a pinch.
+    DCHECK(IsContinuousGestureEvent(new_event->event().GetType()));
+
+    // If there is only one event in the queue we will still emit two events
+    // (scroll and pinch) but the |new_event| will still be coalesced into the
+    // |last_event|, but there will be only one LatencyInfo that should be
+    // traced for two events. In this case we will output an empty LatencyInfo.
+    //
+    // However with two events one will be a GesturePinchUpdate and one will be
+    // a GestureScrollUpdate and we will use the two non-coalesced event's
+    // trace_ids to instrument the flow through the system.
+    int64_t oldest_scroll_trace_id = -1;
+    int64_t oldest_pinch_trace_id = -1;
+    ui::LatencyInfo oldest_latency;
+
+    // Extract the last event in queue (again either a scroll or a pinch).
+    std::unique_ptr<EventWithCallback> last_event = std::move(queue_.back());
+    queue_.pop_back();
+
+    DCHECK(IsContinuousGestureEvent(last_event->event().GetType()));
+
+    SetScrollOrPinchTraceId(last_event.get(), &oldest_scroll_trace_id,
+                            &oldest_pinch_trace_id);
+    oldest_latency = last_event->latency_info();
+    EventWithCallback::OriginalEventList combined_original_events;
+    combined_original_events.splice(combined_original_events.end(),
+                                    last_event->original_events());
+    combined_original_events.splice(combined_original_events.end(),
+                                    new_event->original_events());
+
+    // Extract the second last event in queue IF it's a scroll or a pinch for
+    // the same target.
+    std::unique_ptr<EventWithCallback> second_last_event;
+    if (!queue_.empty() && WebGestureEvent::IsCompatibleScrollorPinch(
+                               ToWebGestureEvent(new_event->event()),
+                               ToWebGestureEvent(queue_.back()->event()))) {
+      second_last_event = std::move(queue_.back());
+      queue_.pop_back();
+      SetScrollOrPinchTraceId(second_last_event.get(), &oldest_scroll_trace_id,
+                              &oldest_pinch_trace_id);
+      oldest_latency = second_last_event->latency_info();
+      combined_original_events.splice(combined_original_events.begin(),
+                                      second_last_event->original_events());
+    }
+
+    // To ensure proper trace tracking we have to determine which event was the
+    // original non-coalesced event. If the event was artificially created (I.E
+    // it sprung into existence in CoalesceScrollAndPinch and isn't associated
+    // with a WebInputEvent that was in the queue) we will give it an empty
+    // LatencyInfo (so it won't have anything reported for it). This can be seen
+    // when a trace_id is equal to -1. We also move the original events into
+    // whichever one is the original non-coalesced event, defaulting to the
+    // pinch event if both are non-coalesced versions so it runs last.
+    ui::LatencyInfo scroll_latency;
+    EventWithCallback::OriginalEventList scroll_original_events;
+    ui::LatencyInfo pinch_latency;
+    EventWithCallback::OriginalEventList pinch_original_events;
+    DCHECK(oldest_pinch_trace_id == -1 || oldest_scroll_trace_id == -1);
+    if (oldest_scroll_trace_id != -1) {
+      scroll_latency = oldest_latency;
+      scroll_latency.set_trace_id(oldest_scroll_trace_id);
+      scroll_original_events = std::move(combined_original_events);
+    } else {
+      // In both the valid pinch event trace id case and scroll and pinch both
+      // have invalid trace_ids case, we will assign original_events to the
+      // pinch_event.
+      pinch_latency = oldest_latency;
+      pinch_latency.set_trace_id(oldest_pinch_trace_id);
+      pinch_original_events = std::move(combined_original_events);
+    }
+
+    TRACE_EVENT2("input", "CoalesceScrollAndPinch", "coalescedTraceId",
+                 new_event->latency_info().trace_id(), "traceId",
+                 scroll_latency.trace_id() != -1 ? scroll_latency.trace_id()
+                                                 : pinch_latency.trace_id());
+    std::pair<std::unique_ptr<WebGestureEvent>,
+              std::unique_ptr<WebGestureEvent>>
+        coalesced_events = WebGestureEvent::CoalesceScrollAndPinch(
+            second_last_event ? &ToWebGestureEvent(second_last_event->event())
+                              : nullptr,
+            ToWebGestureEvent(last_event->event()),
+            ToWebGestureEvent(new_event->event()));
+    DCHECK(coalesced_events.first);
+    DCHECK(coalesced_events.second);
+
+    auto scroll_event = std::make_unique<EventWithCallback>(
+        std::make_unique<WebCoalescedInputEvent>(
+            std::move(coalesced_events.first), scroll_latency),
+        std::move(scroll_original_events));
+    scroll_event->set_coalesced_scroll_and_pinch();
+
+    auto pinch_event = std::make_unique<EventWithCallback>(
+        std::make_unique<WebCoalescedInputEvent>(
+            std::move(coalesced_events.second), pinch_latency),
+        std::move(pinch_original_events));
+    pinch_event->set_coalesced_scroll_and_pinch();
+
+    queue_.push_back(std::move(scroll_event));
+    queue_.push_back(std::move(pinch_event));
   }
-  queue_.push_back(std::move(new_event));
 }
 
 std::unique_ptr<EventWithCallback> CompositorThreadEventQueue::Pop() {
diff --git a/third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.h b/third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.h
index d3c115b..17d1433 100644
--- a/third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.h
+++ b/third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.h
@@ -28,7 +28,8 @@
       delete;
   ~CompositorThreadEventQueue();
 
-  // Adds an event to the queue.
+  // Adds an event to the queue. The event may be coalesced with the last event
+  // if kRefactorCompositorThreadEventQueue is disabled.
   void Queue(std::unique_ptr<EventWithCallback> event);
 
   std::unique_ptr<EventWithCallback> Pop();
diff --git a/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc b/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
index 52f7b5c2a7..f66a089 100644
--- a/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
+++ b/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
@@ -281,8 +281,11 @@
       tick_clock_(base::DefaultTickClock::GetInstance()),
       snap_fling_controller_(std::make_unique<cc::SnapFlingController>(this)),
       cursor_control_handler_(std::make_unique<CursorControlHandler>()),
-      update_scroll_predictor_(base::FeatureList::IsEnabled(
-          input::features::kUpdateScrollPredictorInputMapping)) {
+      update_scroll_predictor_(
+          base::FeatureList::IsEnabled(
+              input::features::kUpdateScrollPredictorInputMapping) &&
+          base::FeatureList::IsEnabled(
+              features::kRefactorCompositorThreadEventQueue)) {
   DCHECK(client);
   input_handler_->BindToClient(this);
 
@@ -649,9 +652,12 @@
     return false;
   }
 
-  // Don't dispatch events that are for a future frame.
-  if (compositor_event_queue_->PeekTimestamp() > sample_time) {
-    return false;
+  if (base::FeatureList::IsEnabled(
+          features::kRefactorCompositorThreadEventQueue)) {
+    // Don't dispatch events that are for a future frame.
+    if (compositor_event_queue_->PeekTimestamp() > sample_time) {
+      return false;
+    }
   }
 
   return true;
@@ -661,7 +667,10 @@
   //  Coalesce all events in the queue before dispatching.
   auto sample_time = base::TimeTicks::Max();
 
-  compositor_event_queue_->CoalesceEvents(sample_time);
+  if (base::FeatureList::IsEnabled(
+          features::kRefactorCompositorThreadEventQueue)) {
+    compositor_event_queue_->CoalesceEvents(sample_time);
+  }
   while (HasQueuedEventsReadyForDispatch(frame_aligned, sample_time)) {
     DispatchSingleInputEvent(compositor_event_queue_->Pop());
   }
@@ -1743,9 +1752,12 @@
 void InputHandlerProxy::ProcessQueuedEventsUpToSampleTime(
     const viz::BeginFrameArgs& args,
     base::TimeTicks sample_time) {
-  // Coalesce scroll and pinch events in the |compositor_event_queue_| till
-  // sample_time.
-  compositor_event_queue_->CoalesceEvents(sample_time);
+  if (base::FeatureList::IsEnabled(
+          features::kRefactorCompositorThreadEventQueue)) {
+    // Coalesce scroll and pinch events in the |compositor_event_queue_| till
+    // sample_time.
+    compositor_event_queue_->CoalesceEvents(sample_time);
+  }
 
   while (HasQueuedEventsReadyForDispatch(true /*frame_aligned*/, sample_time)) {
     auto event_with_callback = compositor_event_queue_->Pop();
diff --git a/third_party/blink/renderer/platform/widget/input/input_handler_proxy.h b/third_party/blink/renderer/platform/widget/input/input_handler_proxy.h
index e86ad51..16713d6 100644
--- a/third_party/blink/renderer/platform/widget/input/input_handler_proxy.h
+++ b/third_party/blink/renderer/platform/widget/input/input_handler_proxy.h
@@ -474,7 +474,10 @@
   // has started, or completed.
   bool enqueue_scroll_events_ = true;
 
-  // Cached value of the kUpdateScrollPredictorInputMapping feature flag.
+  // Cached value of the (kUpdateScrollPredictorInputMapping &
+  // kRefactorCompositorThreadEventQueue) feature flag. (Feature
+  // UpdateScrollPredictorInputMapping needs RefactorCompositorThreadEventQueue
+  // to be enabled).
   const bool update_scroll_predictor_;
 
   // `cc::InputHandlerClient::ScrollEventDispatchMode::kEnqueueScrollEvents`:
diff --git a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
index 31d2136..debccd2f 100644
--- a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
+++ b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/containers/circular_deque.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
+#include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
@@ -27,6 +28,7 @@
 #include "components/input/features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
 #include "third_party/blink/public/common/input/web_gesture_device.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
@@ -301,14 +303,32 @@
 
 class InputHandlerProxyEventQueueTest
     : public testing::Test,
-      public ::testing::WithParamInterface<bool> {
+      public ::testing::WithParamInterface<std::tuple<bool, bool>> {
  public:
   InputHandlerProxyEventQueueTest() = default;
 
   void SetUp() override {
-    scoped_feature_list_.InitWithFeatureState(
-        input::features::kUpdateScrollPredictorInputMapping,
-        /* enabled= */ GetParam());
+    std::vector<base::test::FeatureRefAndParams> enabled_features;
+    std::vector<base::test::FeatureRef> disabled_features;
+
+    if (std::get<0>(GetParam())) {
+      enabled_features.push_back(
+          {input::features::kUpdateScrollPredictorInputMapping, {}});
+    } else {
+      disabled_features.push_back(
+          input::features::kUpdateScrollPredictorInputMapping);
+    }
+
+    if (std::get<1>(GetParam())) {
+      enabled_features.push_back(
+          {features::kRefactorCompositorThreadEventQueue, {}});
+    } else {
+      disabled_features.push_back(
+          features::kRefactorCompositorThreadEventQueue);
+    }
+
+    scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features,
+                                                       disabled_features);
 
     input_handler_proxy_ = std::make_unique<TestInputHandlerProxy>(
         mock_input_handler_, &mock_client_);
@@ -318,6 +338,14 @@
 
   ~InputHandlerProxyEventQueueTest() override = default;
 
+  bool IsUpdateScrollPredictorInputMappingEnabled() const {
+    return std::get<0>(GetParam());
+  }
+
+  bool IsRefactorCompositorThreadEventQueueEnabled() const {
+    return std::get<1>(GetParam());
+  }
+
   void HandleGestureEvent(WebInputEvent::Type type,
                           float delta_y_or_scale = 0,
                           int x = 0,
@@ -1130,8 +1158,7 @@
 }
 
 TEST_P(InputHandlerProxyEventQueueTest, DeliverInputForDeadlineIsScheduled) {
-  // GetParam() is true when kUpdateScrollPredictorInputMapping is enabled.
-  if (GetParam()) {
+  if (IsUpdateScrollPredictorInputMappingEnabled()) {
     return;
   }
 
@@ -1360,7 +1387,7 @@
   enabled_features.push_back({::features::kSendEmptyGestureScrollUpdate,
                               {{"filter_out_empty_updates", "true"}}});
 
-  if (GetParam()) {
+  if (IsUpdateScrollPredictorInputMappingEnabled()) {
     // kUpdateScrollPredictorInputMapping is ENABLED. Disable synthetic
     // prediction for this test.
     enabled_features.push_back(
@@ -2377,15 +2404,29 @@
 
   HandleGestureEvent(WebInputEvent::Type::kGestureScrollUpdate, -40);
 
-  // Second GestureScrollUpdate will be queued without coalescing yet.
-  EXPECT_EQ(2ul, event_queue().size());
+  // The event queue size varies based on the RefactorCompositorThreadEventQueue
+  // feature, which affects event coalescing and queueing, thus influencing
+  // UpdateScrollPredictorInputMapping behavior.
+  if (IsRefactorCompositorThreadEventQueueEnabled()) {
+    // Second GestureScrollUpdate will be queued without coalescing yet.
+    EXPECT_EQ(2ul, event_queue().size());
+  } else {
+    EXPECT_EQ(1ul, event_queue().size());
+  }
   EXPECT_EQ(1ul, event_disposition_recorder_.size());
 
   EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(0);
   HandleGestureEvent(WebInputEvent::Type::kGestureScrollEnd);
 
-  // GestureScrollEnd will be queued.
-  EXPECT_EQ(3ul, event_queue().size());
+  // The event queue size varies based on the
+  // RefactorCompositorThreadEventQueue feature, which affects event
+  // coalescing and queueing, thus influencing
+  // UpdateScrollPredictorInputMapping behavior.
+  if (IsRefactorCompositorThreadEventQueueEnabled()) {
+    EXPECT_EQ(3ul, event_queue().size());
+  } else {
+    EXPECT_EQ(2ul, event_queue().size());
+  }
   EXPECT_EQ(1ul, event_disposition_recorder_.size());
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
@@ -2504,7 +2545,14 @@
   HandleGestureEvent(WebInputEvent::Type::kGestureScrollUpdate, -5);
   HandleGestureEvent(WebInputEvent::Type::kGestureScrollEnd);
 
-  EXPECT_EQ(10ul, event_queue().size());
+  // The event queue size varies based on the RefactorCompositorThreadEventQueue
+  // feature, which affects event coalescing and queueing, thus influencing
+  // UpdateScrollPredictorInputMapping behavior.
+  if (IsRefactorCompositorThreadEventQueueEnabled()) {
+    EXPECT_EQ(10ul, event_queue().size());
+  } else {
+    EXPECT_EQ(8ul, event_queue().size());
+  }
   EXPECT_EQ(2ul, event_disposition_recorder_.size());
 
   GetInputHandlerProxy()->DispatchQueuedInputEventsHelper();
@@ -2657,8 +2705,15 @@
   HandleGestureEventWithSourceDevice(WebInputEvent::Type::kGesturePinchEnd,
                                      WebGestureDevice::kTouchpad);
 
-  // All the events are simply queued.
-  EXPECT_EQ(4ul, event_queue().size());
+  // The event queue size varies based on the RefactorCompositorThreadEventQueue
+  // feature, which affects event coalescing and queueing, thus influencing
+  // UpdateScrollPredictorInputMapping behavior.
+  if (IsRefactorCompositorThreadEventQueueEnabled()) {
+    // All the events are simply queued.
+    EXPECT_EQ(4ul, event_queue().size());
+  } else {
+    EXPECT_EQ(3ul, event_queue().size());
+  }
   EXPECT_EQ(1ul, event_disposition_recorder_.size());
 
   EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(1.21f, _));
@@ -2739,9 +2794,17 @@
       trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END),
       &end_events);
 
-  // All 12 queued events should have a trace slice.
-  EXPECT_EQ(12ul, begin_events.size());
-  EXPECT_EQ(12ul, end_events.size());
+  // The number of trace events depends on whether the
+  // RefactorCompositorThreadEventQueue feature is enabled, as it changes how
+  // events are processed and traced, impacting the behavior of
+  // UpdateScrollPredictorInputMapping.
+  if (IsRefactorCompositorThreadEventQueueEnabled()) {
+    EXPECT_EQ(12ul, begin_events.size());
+    EXPECT_EQ(12ul, end_events.size());
+  } else {
+    EXPECT_EQ(7ul, begin_events.size());
+    EXPECT_EQ(7ul, end_events.size());
+  }
 
   // Filter for only the events that were dispatched.
   trace_analyzer::TraceEventVector dispatched_events;
@@ -3112,8 +3175,7 @@
   // This test verifies the legacy deadline-based dispatch
   // mode, which is superseded by the kUpdateScrollPredictorInputMapping
   // feature. It should only run when the new feature is disabled.
-  if (GetParam()) {  // GetParam() is true when the feature
-                     // UpdateScrollPredictorInputMapping is enabled.
+  if (IsUpdateScrollPredictorInputMappingEnabled()) {
     return;
   }
 
@@ -3620,10 +3682,16 @@
 INSTANTIATE_TEST_SUITE_P(
     All,
     InputHandlerProxyEventQueueTest,
-    ::testing::Bool(),
-    [](auto& info) {
-      return info.param ? "UpdateScrollPredictorInputMapping_Enabled"
-                        : "UpdateScrollPredictorInputMapping_Disabled";
+    testing::Combine(testing::Bool(), testing::Bool()),
+    [](const testing::TestParamInfo<std::tuple<bool, bool>>& info) {
+      return base::StringPrintf(
+          "%s_%s",
+          std::get<0>(info.param)
+              ? "UpdateScrollPredictorInputMapping_Enabled"
+              : "UpdateScrollPredictorInputMapping_Disabled",
+          std::get<1>(info.param)
+              ? "RefactorCompositorThreadEventQueue_Enabled"
+              : "RefactorCompositorThreadEventQueue_Disabled");
     });
 
 INSTANTIATE_TEST_SUITE_P(All,
diff --git a/third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.cc b/third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.cc
index 2c3df7f..bb09f2c 100644
--- a/third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.cc
+++ b/third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.cc
@@ -15,6 +15,18 @@
 
 namespace blink {
 
+struct SynchronousCompositorProxy::SharedMemoryWithSize {
+  base::WritableSharedMemoryMapping shared_memory;
+  const size_t buffer_size;
+  bool zeroed;
+
+  SharedMemoryWithSize(base::WritableSharedMemoryMapping shm_mapping,
+                       size_t buffer_size)
+      : shared_memory(std::move(shm_mapping)),
+        buffer_size(buffer_size),
+        zeroed(true) {}
+};
+
 SynchronousCompositorProxy::SynchronousCompositorProxy(
     InputHandlerProxy* input_handler_proxy)
     : input_handler_proxy_(input_handler_proxy),
@@ -144,18 +156,6 @@
   }
 }
 
-struct SynchronousCompositorProxy::SharedMemoryWithSize {
-  base::WritableSharedMemoryMapping shared_memory;
-  const size_t buffer_size;
-  bool zeroed;
-
-  SharedMemoryWithSize(base::WritableSharedMemoryMapping shm_mapping,
-                       size_t buffer_size)
-      : shared_memory(std::move(shm_mapping)),
-        buffer_size(buffer_size),
-        zeroed(true) {}
-};
-
 void SynchronousCompositorProxy::ZeroSharedMemory() {
   // It is possible for this to get called twice, eg. if draw is called before
   // the LayerTreeFrameSink is ready. Just ignore duplicated calls rather than
diff --git a/third_party/blink/renderer/platform/wtf/text/atomic_string.h b/third_party/blink/renderer/platform/wtf/text/atomic_string.h
index 317c07b..0d4bfa60 100644
--- a/third_party/blink/renderer/platform/wtf/text/atomic_string.h
+++ b/third_party/blink/renderer/platform/wtf/text/atomic_string.h
@@ -86,10 +86,6 @@
   StringImpl* Impl() const { return string_.Impl(); }
 
   bool Is8Bit() const { return string_.Is8Bit(); }
-  // Use Span16() instead.
-  UNSAFE_BUFFER_USAGE const UChar* Characters16() const {
-    return UNSAFE_TODO(string_.Characters16());
-  }
   wtf_size_t length() const { return string_.length(); }
   base::span<const LChar> Span8() const { return string_.Span8(); }
   base::span<const UChar> Span16() const { return string_.Span16(); }
diff --git a/third_party/blink/renderer/platform/wtf/text/case_folding_hash.h b/third_party/blink/renderer/platform/wtf/text/case_folding_hash.h
index 6ae1ff2..f30c304 100644
--- a/third_party/blink/renderer/platform/wtf/text/case_folding_hash.h
+++ b/third_party/blink/renderer/platform/wtf/text/case_folding_hash.h
@@ -19,16 +19,12 @@
  *
  */
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_CASE_FOLDING_HASH_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_CASE_FOLDING_HASH_H_
 
 // Case-insensitive hash lookups, using the Unicode case folding algorithm.
 
+#include "base/containers/span.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
 #include "third_party/blink/renderer/platform/wtf/text/unicode.h"
@@ -60,13 +56,15 @@
 
   static inline uint64_t Read64(const uint8_t* ptr) {
     const T* p = reinterpret_cast<const T*>(ptr);
-    return FoldCase(p[0]) | (FoldCase(p[1]) << 16) | (FoldCase(p[2]) << 32) |
-           (FoldCase(p[3]) << 48);
+    // SAFETY: We expect the rapidhash library provides enough size for `ptr`.
+    return UNSAFE_BUFFERS(FoldCase(p[0]) | (FoldCase(p[1]) << 16) |
+                          (FoldCase(p[2]) << 32) | (FoldCase(p[3]) << 48));
   }
 
   static inline uint64_t Read32(const uint8_t* ptr) {
     const T* p = reinterpret_cast<const T*>(ptr);
-    return FoldCase(p[0]) | (FoldCase(p[1]) << 16);
+    // SAFETY: We expect the rapidhash library provides enough size for `ptr`.
+    return UNSAFE_BUFFERS(FoldCase(p[0]) | (FoldCase(p[1]) << 16));
   }
 
   static inline uint64_t ReadSmall(const uint8_t* ptr, size_t k) {
@@ -98,31 +96,26 @@
   STATIC_ONLY(CaseFoldingHash);
 
  public:
-  static unsigned GetHash(const UChar* data, unsigned length) {
+  static unsigned GetHash(base::span<const UChar> span) {
     return StringHasher::ComputeHashAndMaskTop8Bits<
-        CaseFoldingHashReader<UChar>>(reinterpret_cast<const char*>(data),
-                                      length * 2);
+        CaseFoldingHashReader<UChar>>(
+        reinterpret_cast<const char*>(span.data()), span.size() * 2);
   }
 
   static unsigned GetHash(StringImpl* str) {
     if (str->Is8Bit())
-      return GetHash(str->Characters8(), str->length());
-    return GetHash(str->Characters16(), str->length());
+      return GetHash(str->Span8());
+    return GetHash(str->Span16());
   }
 
-  static unsigned GetHash(const LChar* data, unsigned length) {
+  static unsigned GetHash(base::span<const LChar> span) {
     return StringHasher::ComputeHashAndMaskTop8Bits<
-        CaseFoldingHashReader<LChar>>(reinterpret_cast<const char*>(data),
-                                      length * 2);
+        CaseFoldingHashReader<LChar>>(base::as_chars(span).data(),
+                                      span.size() * 2);
   }
 
-  static inline unsigned GetHash(const char* data, unsigned length) {
-    return GetHash(reinterpret_cast<const LChar*>(data), length);
-  }
-
-  static inline unsigned GetHash(const char* data) {
-    return GetHash(reinterpret_cast<const LChar*>(data),
-                   static_cast<unsigned>(strlen(data)));
+  static inline unsigned GetHash(base::span<const char> span) {
+    return GetHash(base::as_byte_span(span));
   }
 
   static inline bool Equal(const StringImpl* a, const StringImpl* b) {
diff --git a/third_party/blink/renderer/platform/wtf/text/string_hasher.h b/third_party/blink/renderer/platform/wtf/text/string_hasher.h
index 2b33f9c0..c689ef4 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_hasher.h
+++ b/third_party/blink/renderer/platform/wtf/text/string_hasher.h
@@ -52,8 +52,7 @@
   // that is not 8-bit elements, and do _not_ use compression factors or
   // similar, you'll need to multiply by sizeof(T) to get all data read.
   template <class Reader = PlainHashReader>
-  static unsigned ComputeHashAndMaskTop8Bits(const char* data,
-                                             unsigned length) {
+  static unsigned ComputeHashAndMaskTop8Bits(const char* data, size_t length) {
     return MaskTop8Bits(
         rapidhash<Reader>(reinterpret_cast<const uint8_t*>(data), length));
   }
diff --git a/third_party/blink/renderer/platform/wtf/text/string_hasher_test.cc b/third_party/blink/renderer/platform/wtf/text/string_hasher_test.cc
index 6e8f23d..a88149e 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_hasher_test.cc
+++ b/third_party/blink/renderer/platform/wtf/text/string_hasher_test.cc
@@ -48,6 +48,14 @@
 const uint64_t kTestAHash = 0xE9422771E0A5DDE6;
 const uint64_t kTestBHash = 0x4A2DA770EEA75C1E;
 
+bool EqualCaseFoldingHash(StringView a, StringView b) {
+  unsigned hash_a = a.Is8Bit() ? CaseFoldingHash::GetHash(a.Span8())
+                               : CaseFoldingHash::GetHash(a.Span16());
+  unsigned hash_b = b.Is8Bit() ? CaseFoldingHash::GetHash(b.Span8())
+                               : CaseFoldingHash::GetHash(b.Span16());
+  return hash_a == hash_b;
+}
+
 }  // anonymous namespace
 
 TEST(StringHasherTest, StringHasher_ComputeHashAndMaskTop8Bits) {
@@ -109,13 +117,12 @@
 }
 
 TEST(StringHasherTest, CaseFoldingHash) {
-  EXPECT_NE(CaseFoldingHash::GetHash("foo"), CaseFoldingHash::GetHash("bar"));
-  EXPECT_EQ(CaseFoldingHash::GetHash("foo"), CaseFoldingHash::GetHash("FOO"));
-  EXPECT_EQ(CaseFoldingHash::GetHash("foo"), CaseFoldingHash::GetHash("Foo"));
-  EXPECT_EQ(CaseFoldingHash::GetHash("Longer string 123"),
-            CaseFoldingHash::GetHash("longEr String 123"));
-  EXPECT_EQ(CaseFoldingHash::GetHash(String::FromUTF8("Ünicode")),
-            CaseFoldingHash::GetHash(String::FromUTF8("ünicode")));
+  EXPECT_FALSE(EqualCaseFoldingHash("foo", "bar"));
+  EXPECT_TRUE(EqualCaseFoldingHash("foo", "FOO"));
+  EXPECT_TRUE(EqualCaseFoldingHash("foo", "Foo"));
+  EXPECT_TRUE(EqualCaseFoldingHash("Longer string 123", "longEr String 123"));
+  EXPECT_TRUE(EqualCaseFoldingHash(String::FromUTF8("Ünicode"),
+                                   String::FromUTF8("ünicode")));
 }
 
 TEST(StringHasherTest, ContractionAndExpansion) {
diff --git a/third_party/blink/renderer/platform/wtf/vector_test.cc b/third_party/blink/renderer/platform/wtf/vector_test.cc
index 90416bc..04c4ed0 100644
--- a/third_party/blink/renderer/platform/wtf/vector_test.cc
+++ b/third_party/blink/renderer/platform/wtf/vector_test.cc
@@ -23,11 +23,6 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 #include <memory>
@@ -151,7 +146,7 @@
   EXPECT_EQ(int_vector.end(), end);
 
   auto item2 = std::lower_bound(int_vector.begin(), int_vector.end(), 2);
-  auto item4 = int_vector.erase(item2, item2 + 2);
+  auto item4 = int_vector.erase(item2, UNSAFE_TODO(item2 + 2));
   EXPECT_EQ(2u, int_vector.size());
   EXPECT_EQ(4, *item4);
 
@@ -188,13 +183,13 @@
   EXPECT_TRUE(end != it);
 
   EXPECT_EQ(10, *it);
-  ++it;
+  UNSAFE_TODO(++it);
   EXPECT_EQ(11, *it);
-  ++it;
+  UNSAFE_TODO(++it);
   EXPECT_EQ(12, *it);
-  ++it;
+  UNSAFE_TODO(++it);
   EXPECT_EQ(13, *it);
-  ++it;
+  UNSAFE_TODO(++it);
 
   EXPECT_TRUE(end == it);
 }
@@ -239,7 +234,7 @@
 
   wtf_size_t index = 0;
   for (OwnPtrVector::iterator iter = vector.begin(); iter != vector.end();
-       ++iter) {
+       UNSAFE_TODO(++iter)) {
     std::unique_ptr<DestructCounter>& ref_counter = *iter;
     EXPECT_EQ(index, static_cast<wtf_size_t>(ref_counter.get()->Get()));
     EXPECT_EQ(index, static_cast<wtf_size_t>(ref_counter->Get()));
@@ -381,36 +376,36 @@
   vector_a.reserve(32);
 
   volatile int* int_pointer_a = vector_a.data();
-  EXPECT_DEATH(int_pointer_a[1] = 11, "container-overflow");
+  EXPECT_DEATH(UNSAFE_TODO(int_pointer_a[1]) = 11, "container-overflow");
   vector_a.push_back(11);
-  int_pointer_a[1] = 11;
-  EXPECT_DEATH(int_pointer_a[2] = 12, "container-overflow");
-  EXPECT_DEATH((void)int_pointer_a[2], "container-overflow");
+  UNSAFE_TODO(int_pointer_a[1]) = 11;
+  EXPECT_DEATH(UNSAFE_TODO(int_pointer_a[2]) = 12, "container-overflow");
+  EXPECT_DEATH((void)UNSAFE_TODO(int_pointer_a[2]), "container-overflow");
   vector_a.shrink_to_fit();
   vector_a.reserve(16);
   int_pointer_a = vector_a.data();
-  EXPECT_DEATH((void)int_pointer_a[2], "container-overflow");
+  EXPECT_DEATH((void)UNSAFE_TODO(int_pointer_a[2]), "container-overflow");
 
   Vector<int> vector_b(vector_a);
   vector_b.reserve(16);
   volatile int* int_pointer_b = vector_b.data();
-  EXPECT_DEATH((void)int_pointer_b[2], "container-overflow");
+  EXPECT_DEATH((void)UNSAFE_TODO(int_pointer_b[2]), "container-overflow");
 
   Vector<int> vector_c((Vector<int>(vector_a)));
   volatile int* int_pointer_c = vector_c.data();
-  EXPECT_DEATH((void)int_pointer_c[2], "container-overflow");
+  EXPECT_DEATH((void)UNSAFE_TODO(int_pointer_c[2]), "container-overflow");
   vector_c.push_back(13);
   vector_c.swap(vector_b);
 
   volatile int* int_pointer_b2 = vector_b.data();
   volatile int* int_pointer_c2 = vector_c.data();
-  int_pointer_b2[2] = 13;
-  EXPECT_DEATH((void)int_pointer_b2[3], "container-overflow");
-  EXPECT_DEATH((void)int_pointer_c2[2], "container-overflow");
+  UNSAFE_TODO(int_pointer_b2[2]) = 13;
+  EXPECT_DEATH((void)UNSAFE_TODO(int_pointer_b2[3]), "container-overflow");
+  EXPECT_DEATH((void)UNSAFE_TODO(int_pointer_c2[2]), "container-overflow");
 
   vector_b = vector_c;
   volatile int* int_pointer_b3 = vector_b.data();
-  EXPECT_DEATH((void)int_pointer_b3[2], "container-overflow");
+  EXPECT_DEATH((void)UNSAFE_TODO(int_pointer_b3[2]), "container-overflow");
 }
 #endif  // defined(ANNOTATE_CONTIGUOUS_CONTAINER)
 
diff --git a/third_party/blink/tools/blinkpy/common/config/builders.json b/third_party/blink/tools/blinkpy/common/config/builders.json
index 93c440d..3284bd20 100644
--- a/third_party/blink/tools/blinkpy/common/config/builders.json
+++ b/third_party/blink/tools/blinkpy/common/config/builders.json
@@ -51,6 +51,7 @@
             "blink_web_tests": {},
             "blink_wpt_tests": {},
             "chrome_wpt_tests": {},
+            "headless_shell_wpt_tests": {},
             "high_dpi_blink_web_tests": {
                 "flag_specific": "highdpi"
             },
@@ -264,8 +265,8 @@
     },
     "mac-rel": {
         "main": "tryserver.chromium.mac",
-        "port_name": "mac-mac14",
-        "specifiers": ["Mac14", "Release"],
+        "port_name": "mac-mac15",
+        "specifiers": ["Mac15", "Release"],
         "steps": {
             "blink_web_tests": {},
             "blink_wpt_tests": {},
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
index da8a78a..7e5b31f 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
@@ -104,6 +104,13 @@
         default=False,
         help=('Dry run mode. List actions that would be performed '
               'but do not actually write to disk.'))
+    clobber_os_version_option = optparse.make_option(
+        '--clobber-os-version',
+        action='store_true',
+        default=False,
+        help=('Write baselines directly to `platform/$os/` instead of '
+              '`platform/$os-$version/` by assuming the tests are OS version-'
+              'agnostic.'))
     results_directory_option = optparse.make_option(
         '--results-directory',
         action='callback',
@@ -339,6 +346,7 @@
         self.baseline_cache_stats = BaselineCacheStatistics()
         self._total_commands = self._completed_commands = 0
         self._rebaseline_failures = {}
+        self._clobber_os_version = False
 
     def _release_builders(self):
         """Returns a list of builder names for continuous release builders.
@@ -415,6 +423,8 @@
         return build_steps
 
     def _copy_baselines(self, groups: Dict[str, TestBaselineSet]) -> None:
+        if self._clobber_os_version:
+            return
         commands = []
         for base_test in sorted(groups):
             group = groups[base_test]
@@ -538,7 +548,9 @@
             pool.run(commands)
 
     def _worker_factory(self, worker_connection):
-        return Worker(worker_connection, dry_run=self._dry_run)
+        return Worker(worker_connection,
+                      dry_run=self._dry_run,
+                      clobber_os_version=self._clobber_os_version)
 
     def handle(self, name: str, source: str, *args):
         """Handler called when a worker completes a rebaseline task.
@@ -997,9 +1009,11 @@
 
     def __init__(self,
                  connection,
-                 dry_run: bool = False):
+                 dry_run: bool = False,
+                 clobber_os_version: bool = False):
         self._connection = connection
         self._dry_run = dry_run
+        self._clobber_os_version = clobber_os_version
         self._commands = {
             'copy_baselines': self._copy_baselines,
             'download_baselines': self._download_baselines,
@@ -1081,8 +1095,13 @@
         flag_spec_option = self._host.builders.flag_specific_option(
             task.build.builder_name, task.step_name)
         port.set_option_default('flag_specific', flag_spec_option)
+        if self._clobber_os_version:
+            version_dir = self._fs.join(port.web_tests_dir(), 'platform',
+                                        port.operating_system())
+        else:
+            version_dir = port.baseline_version_dir()
         dest = self._fs.join(
-            port.baseline_version_dir(),
+            version_dir,
             port.output_filename(task.test,
                                  test_failures.FILENAME_SUFFIX_EXPECTED,
                                  '.' + suffix))
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl.py
index fe32973..1276f80 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl.py
@@ -87,6 +87,7 @@
 
     def __init__(self, tool, io_pool: Optional[Executor] = None):
         super().__init__(options=[
+            self.clobber_os_version_option,
             self.only_changed_tests_option,
             self.no_trigger_jobs_option,
             self.test_name_file_option,
@@ -137,6 +138,7 @@
 
     def execute(self, options, args, tool):
         self._dry_run = options.dry_run
+        self._clobber_os_version = options.clobber_os_version
         self.git_cl = self.git_cl or GitCL(tool)
 
         # '--dry-run' implies '--no-trigger-jobs'.
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py
index ccd5400..96cf63f 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py
@@ -242,6 +242,7 @@
             'issue': None,
             'patchset': None,
             'manifest_update': False,
+            'clobber_os_version': False,
         }
         options.update(kwargs)
         return optparse.Values(options)
@@ -333,6 +334,24 @@
             'INFO: Staging 0 baselines with git.\n',
         ])
 
+    def test_execute_with_clobber_os_version(self):
+        exit_code = self.command.execute(
+            self.command_options(clobber_os_version=True), [], self.tool)
+        self.assertEqual(exit_code, 0)
+        self.assertLog([
+            'INFO: Fetching status for 4 builds from https://crrev.com/c/1234.\n',
+            'INFO: All builds finished.\n',
+            'INFO: Fetching test results for 4 suites.\n',
+            'INFO: Rebaselining 5 tests.\n',
+            # No copy steps.
+            "INFO: Downloaded baselines for 'one/flaky-fail.html' (1/5)\n",
+            "INFO: Downloaded baselines for 'one/missing.html' (2/5)\n",
+            "INFO: Downloaded baselines for 'one/slow-fail.html' (3/5)\n",
+            "INFO: Downloaded baselines for 'one/text-fail.html' (4/5)\n",
+            "INFO: Downloaded baselines for 'two/image-fail.html' (5/5)\n",
+            'INFO: Staging 0 baselines with git.\n',
+        ])
+
     def test_execute_basic_dry_run(self):
         """Dry running does not execute any commands or write any files."""
         self.set_logging_level(logging.DEBUG)
diff --git a/third_party/blink/tools/blinkpy/w3c/export_notifier.py b/third_party/blink/tools/blinkpy/w3c/export_notifier.py
index d817d21..e314517 100644
--- a/third_party/blink/tools/blinkpy/w3c/export_notifier.py
+++ b/third_party/blink/tools/blinkpy/w3c/export_notifier.py
@@ -14,8 +14,8 @@
 import logging
 from typing import Mapping
 
-from blinkpy.w3c.common import WPT_REVISION_FOOTER, WPT_GH_URL
-from blinkpy.w3c.gerrit import GerritError
+from blinkpy.w3c.common import CHANGE_ID_FOOTER, WPT_REVISION_FOOTER, WPT_GH_URL
+from blinkpy.w3c.gerrit import GerritCL, GerritError
 from blinkpy.w3c.wpt_github import GitHubError
 
 _log = logging.getLogger(__name__)
@@ -26,6 +26,11 @@
 
 
 class ExportNotifier(object):
+    OWNERS_TEAMS = {
+        'interop',
+        'wpt-core-team',
+    }
+
     def __init__(self, host, wpt_github, gerrit, dry_run=True):
         self.host = host
         self.wpt_github = wpt_github
@@ -67,7 +72,7 @@
             gerrit_id = self.wpt_github.extract_metadata(
                 'Change-Id: ', pr.body)
             if not gerrit_id:
-                _log.error('Can not retrieve Change-Id for %s.', pr.number)
+                _log.warning('Can not retrieve Change-Id for %s.', pr.number)
                 continue
 
             gerrit_sha = self.wpt_github.extract_metadata(
@@ -78,6 +83,49 @@
         self.process_failing_prs(prs_by_change_id)
         return prs_by_change_id
 
+    def notify_gerrit_of_blocked_pr(self, pull_request):
+        change_id = self.wpt_github.extract_metadata(CHANGE_ID_FOOTER,
+                                                     pull_request.body)
+        if not change_id:
+            _log.warning('Could not find Change-Id for PR #%d',
+                         pull_request.number)
+            return
+
+        # Detect if the PR is pending owner approval. This is based on whether a
+        # review is requested from an owners team.
+        requested_team_slugs = {
+            team.get('slug')
+            for team in pull_request.requested_teams
+        }
+        approving_teams = self.OWNERS_TEAMS.intersection(requested_team_slugs)
+
+        if not approving_teams:
+            _log.info('PR #%d is blocked, but not pending owner approval.',
+                      pull_request.number)
+            return
+
+        message = ('The exported PR for this CL requires approval from the ' +
+                   ', '.join(sorted(approving_teams)) +
+                   ' team(s) on GitHub. Please see the PR for details: ' +
+                   f'{self.wpt_github.url}pull/{pull_request.number}')
+
+        try:
+            cl = self.gerrit.query_cl_comments_and_revisions(change_id)
+            if any(message in m['message'] for m in cl.messages):
+                _log.info(
+                    'A notification for pending approval already exists on CL %s.',
+                    change_id)
+                return
+        except (GerritError, KeyError):
+            _log.exception('Could not retrieve comments for CL %s.',
+                           change_id)
+            return
+
+        _log.info('Posting notification for pending approval to CL %s.',
+                  change_id)
+        if not self.dry_run:
+            cl.post_comment(message)
+
     def get_check_runs(self, number):
         """Retrieves check runs through a PR number.
 
diff --git a/third_party/blink/tools/blinkpy/w3c/export_notifier_unittest.py b/third_party/blink/tools/blinkpy/w3c/export_notifier_unittest.py
index 32333bf..91748d3 100644
--- a/third_party/blink/tools/blinkpy/w3c/export_notifier_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/export_notifier_unittest.py
@@ -371,6 +371,97 @@
                 'message': expected
             })])
 
+    def _create_test_notifier(self):
+        gerrit = MockGerritAPI()
+        github = MockWPTGitHub(pull_requests=[])
+        github.gh_org = 'web-platform-tests'
+        github.gh_repo_name = 'wpt'
+        notifier = ExportNotifier(self.host, github, gerrit, dry_run=False)
+        return notifier, gerrit, github
+
+    def test_notify_gerrit_of_blocked_pr_posts_comment(self):
+        notifier, gerrit, github = self._create_test_notifier()
+        pull_request = PullRequest(title='title',
+                                   number=123,
+                                   body='Change-Id: I123',
+                                   state='open',
+                                   node_id='PR_123_',
+                                   labels=[],
+                                   requested_teams=[{'slug': 'interop'}])
+        message = ('The exported PR for this CL requires approval from the '
+                   'interop team(s) on GitHub. Please see the PR for details: '
+                   'https://github.com/web-platform-tests/wpt/pull/123')
+        gerrit.cl = MockGerritCL(data={
+            'change_id': 'I123',
+            'messages': []
+        },
+                                 api=gerrit)
+        notifier.notify_gerrit_of_blocked_pr(pull_request)
+        self.assertEqual(len(gerrit.request_posted), 1)
+        self.assertIn(message, gerrit.request_posted[0][1]['message'])
+
+    def test_notify_gerrit_of_blocked_pr_no_approval_needed(self):
+        notifier, gerrit, github = self._create_test_notifier()
+        pull_request = PullRequest(title='title',
+                                   number=123,
+                                   body='Change-Id: I123',
+                                   state='open',
+                                   node_id='PR_123_',
+                                   labels=[])
+        github.pr_data = {
+            'head': {
+                'ref': 'branch-name'
+            },
+            'requested_teams': []
+        }
+        gerrit.cl = MockGerritCL(data={
+            'change_id': 'I123',
+            'messages': []
+        },
+                                 api=gerrit)
+        notifier.notify_gerrit_of_blocked_pr(pull_request)
+        print(pull_request)
+        self.assertEqual(len(gerrit.request_posted), 0)
+
+    def test_notify_gerrit_of_blocked_pr_notification_exists(self):
+        notifier, gerrit, github = self._create_test_notifier()
+        pull_request = PullRequest(
+            title='title',
+            number=123,
+            body='Change-Id: I123',
+            state='open',
+            node_id='PR_123_',
+            labels=[],
+            requested_teams=[{
+                'slug': 'interop'
+            }, {
+                'slug': 'wpt-core-team'
+            }])
+        message = ('The exported PR for this CL requires approval from the '
+                   'interop, wpt-core-team team(s) on GitHub. Please see the '
+                   'PR for details: '
+                   'https://github.com/web-platform-tests/wpt/pull/123')
+        gerrit.cl = MockGerritCL(data={
+            'change_id': 'I123',
+            'messages': [{
+                'message': message
+            }]
+        },
+                                 api=gerrit)
+        notifier.notify_gerrit_of_blocked_pr(pull_request)
+        self.assertEqual(len(gerrit.request_posted), 0)
+
+    def test_notify_gerrit_of_blocked_pr_no_change_id(self):
+        notifier, gerrit, github = self._create_test_notifier()
+        pull_request = PullRequest(title='title',
+                                   number=123,
+                                   body='No Change-Id here',
+                                   state='open',
+                                   node_id='PR_123_',
+                                   labels=[])
+        notifier.notify_gerrit_of_blocked_pr(pull_request)
+        self.assertEqual(len(gerrit.request_posted), 0)
+
     def generate_notifier_comment(self,
                                   pr_number,
                                   checks_results,
diff --git a/third_party/blink/tools/blinkpy/w3c/test_exporter.py b/third_party/blink/tools/blinkpy/w3c/test_exporter.py
index 4fb8a57..03cb9de 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_exporter.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_exporter.py
@@ -49,6 +49,7 @@
         self.github = None
         self.graphql = None
         self.gerrit = None
+        self.notifier = None
         self.dry_run = False
         self.local_repo = None
         self.surface_failures_to_gerrit = False
@@ -87,6 +88,8 @@
             token=credentials['GH_TOKEN'])
         self.gerrit = self.gerrit or GerritAPI.from_credentials(
             self.host, credentials)
+        self.notifier = ExportNotifier(self.host, self.github, self.gerrit,
+                                       self.dry_run)
         self.local_repo = self.local_repo or self.project_config.local_repo_factory(
             host=self.host, gh_token=credentials['GH_TOKEN'])
 
@@ -122,9 +125,7 @@
             if self.surface_failures_to_gerrit:
                 _log.info(
                     'Starting surfacing cross-browser failures to Gerrit.')
-                notifier = ExportNotifier(self.host, self.github, self.gerrit,
-                                          self.dry_run)
-                prs_by_change_id = notifier.main()
+                prs_by_change_id = self.notifier.main()
                 pr_events[PREventType.BLOCKED].update(
                     pr_status.pr_number
                     for pr_status in prs_by_change_id.values())
@@ -335,6 +336,7 @@
                 return PREvent(pull_request.number, PREventType.MERGED)
         except MergeError:
             _log.warn('Could not merge PR.')
+            self.notifier.notify_gerrit_of_blocked_pr(pull_request)
             return PREvent(pull_request.number, PREventType.BLOCKED)
         return None
 
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer.py b/third_party/blink/tools/blinkpy/w3c/test_importer.py
index 98269700..19b3f24 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer.py
@@ -649,9 +649,13 @@
 
         # If this starts blocking the importer unnecessarily, revert
         # https://chromium-review.googlesource.com/c/chromium/src/+/2451504
-        # Try linux-blink-rel to make sure no breakage in webdriver tests
-        for builder in ['linux-blink-rel']:
-            description += f'Cq-Include-Trybots: luci.chromium.try:{builder}\n'
+        # Try `*-blink-rel` to make sure no breakage in webdriver tests or for
+        # any OS versions not covered in CQ.
+        for builder in self.host.builders.all_try_builder_names():
+            if self.host.builders.main_for_builder(
+                    builder) == 'tryserver.blink':
+                description += (
+                    f'Cq-Include-Trybots: luci.chromium.try:{builder}\n')
 
         return description
 
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
index 9543db1..5d3a704 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
@@ -58,6 +58,7 @@
         host = MockHost()
         host.builders = BuilderList({
             'cq-builder-a': {
+                'main': 'tryserver.blink',
                 'port_name': 'linux-trusty',
                 'specifiers': ['Trusty', 'Release'],
                 'steps': {
@@ -66,6 +67,7 @@
                 'is_try_builder': True,
             },
             'cq-builder-b': {
+                'main': 'tryserver.blink',
                 'port_name': 'mac-mac12',
                 'specifiers': ['Mac12', 'Release'],
                 'steps': {
@@ -565,7 +567,8 @@
                 NOAUTOREVERT=true
                 No-Export: true
                 Validate-Test-Flakiness: skip
-                Cq-Include-Trybots: luci.chromium.try:linux-blink-rel
+                Cq-Include-Trybots: luci.chromium.try:cq-builder-a
+                Cq-Include-Trybots: luci.chromium.try:cq-builder-b
                 """))
         self.assertEqual(host.executive.calls, [MANIFEST_INSTALL_CMD] +
                          [['git', 'log', '-1', '--format=%B']])
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
index 028e81b..0076102 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
@@ -59,6 +59,15 @@
         parser = argparse.ArgumentParser(description=__doc__)
         self.add_arguments(parser)
         self.options = parser.parse_args(args or [])
+        self.options.builders = self.options.builders or [
+            'mac-rel',
+            'win-rel',
+            # Use these `*-blink-rel` builders instead of `android-*-rel` and
+            # `linux-rel`, respectively, to update `*webdriver_wpt_tests`
+            # expectations.
+            'android-15-chrome-blink-rel',
+            'linux-blink-rel',
+        ]
         if not (self.options.clean_up_test_expectations or
                 self.options.clean_up_test_expectations_only):
             assert not self.options.clean_up_affected_tests_only, (
@@ -134,6 +143,12 @@
             help='Adds Pass to tests with failure expectations. '
                  'This command line argument can be used to mark tests '
                  'as flaky.')
+        parser.add_argument(
+            '--builder',
+            dest='builders',
+            action='append',
+            help=('Builder name to use for updating expectations. May provide '
+                  'multiple times.'))
 
     def suites_for_builder(self, builder: str) -> Set[str]:
         # TODO(crbug.com/1502294): Make everything a suite name (i.e., without
@@ -163,7 +178,7 @@
         resolver = BuildResolver(self.host.web,
                                  self.git_cl,
                                  can_trigger_jobs=False)
-        builds = [Build(builder) for builder in self._get_try_bots()]
+        builds = [Build(builder) for builder in self.options.builders]
         try:
             issue = self.git_cl.get_issue_number()
             assert issue is not None
@@ -704,8 +719,14 @@
         for test in tests_to_rebaseline:
             _log.info('  %s', test)
 
-        # The importer should have already updated the manifests.
-        args = ['--no-trigger-jobs', '--no-manifest-update']
+        builders = ', '.join(self.options.builders)
+        args = [
+            '--no-trigger-jobs',
+            # The importer should have already updated the manifests.
+            '--no-manifest-update',
+            '--clobber-os-version',
+            f'--builders={builders}'
+        ]
         if self.options.verbose:
             args.append('--verbose')
         if self.patchset:
@@ -714,13 +735,15 @@
         self._run_blink_tool('rebaseline-cl', args)
 
     def _run_blink_tool(self, subcommand: str, args: List[str]):
-        output = self.host.executive.run_command([
+        command = [
             self.host.executable,
             self.finder.path_from_blink_tools('blink_tool.py'),
             subcommand,
             *args,
-        ])
-        _log.info('Output of %s:', subcommand)
+        ]
+        output = self.host.executive.run_command(command)
+        _log.info('Output of %s:',
+                  self.host.executive.command_for_printing(command))
         for line in output.splitlines():
             _log.info('  %s: %s', subcommand, line)
         _log.info('-- end of %s output --', subcommand)
@@ -738,10 +761,3 @@
     def is_reference_test(self, test_name: str) -> bool:
         """Checks whether a given test is a reference test."""
         return bool(self.port.reference_files(test_name))
-
-    @memoized
-    def _get_try_bots(self):
-        builders = self.host.builders.filter_builders(is_try=True)
-        # Exclude CQ builders like `win-rel`.
-        return sorted(
-            set(builders) & self.host.builders.builders_for_rebaselining())
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py
index 831d990..be71308 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py
@@ -139,6 +139,7 @@
 
     def mock_updater(self, host) -> WPTExpectationsUpdater:
         updater = WPTExpectationsUpdater(host)
+        updater.options.builders = host.builders.filter_builders(is_try=True)
         updater.git_cl = MockGitCL(
             host, {
                 Build('MOCK Try Mac10.10', 333, 'Build-1'):
@@ -194,6 +195,7 @@
 
         # Set up fake try job results.
         updater = WPTExpectationsUpdater(host)
+        updater.options.builders = host.builders.filter_builders(is_try=True)
         updater.git_cl = MockGitCL(
             updater.host, {
                 Build('MOCK Try Mac10.10', 333, 'Build-1'):
@@ -245,6 +247,7 @@
             },
         })
         updater = WPTExpectationsUpdater(host)
+        updater.options.builders = host.builders.filter_builders(is_try=True)
         expectations_path = updater.finder.path_from_web_tests(
             'TestExpectations')
         host.filesystem.write_text_file(
@@ -302,6 +305,7 @@
                 """))
         # Set up fake try job results.
         updater = WPTExpectationsUpdater(host)
+        updater.options.builders = host.builders.filter_builders(is_try=True)
         updater.git_cl = MockGitCL(
             updater.host, {
                 Build('MOCK Try Mac10.10', 333, 'Build-1'):
@@ -360,6 +364,7 @@
 
         # Set up fake try job results.
         updater = WPTExpectationsUpdater(host)
+        updater.options.builders = host.builders.filter_builders(is_try=True)
         updater.git_cl = MockGitCL(
             updater.host, {
                 Build('MOCK Try Mac10.10', 333, 'Build-1'):
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_github.py b/third_party/blink/tools/blinkpy/w3c/wpt_github.py
index 734dceed..372c1b8 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_github.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_github.py
@@ -3,10 +3,12 @@
 # found in the LICENSE file.
 
 import base64
+import dataclasses
 import datetime
 import json
 import logging
 import re
+from typing import Any, Dict, List
 from urllib.parse import quote
 
 from collections import namedtuple
@@ -240,7 +242,8 @@
                            body=item['body'],
                            state=item['state'],
                            node_id=item['node_id'],
-                           labels=labels)
+                           labels=labels,
+                           requested_teams=item.get('requested_teams', []))
 
     def recent_failing_chromium_exports(self):
         """Fetches open PRs with an export label, failing status, and updated
@@ -582,5 +585,14 @@
         super(MergeError, self).__init__(200, 405, 'merge PR %d' % pr_number)
 
 
-PullRequest = namedtuple(
-    'PullRequest', ['title', 'number', 'body', 'state', 'node_id', 'labels'])
+@dataclasses.dataclass(frozen=True)
+class PullRequest:
+    """Represents a GitHub pull request."""
+    title: str
+    number: int
+    body: str
+    state: str
+    node_id: str
+    labels: List[str]
+    requested_teams: List[Dict[str, Any]] = dataclasses.field(
+        default_factory=list)
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index b99bb98..e06fb5d 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -784,7 +784,6 @@
 crbug.com/438515315 virtual/css-anchor-update-enabled/external/wpt/css/css-anchor-position/scrollable-containing-block-size.html [ Pass ]
 crbug.com/438515315 virtual/css-anchor-update-enabled/external/wpt/css/css-anchor-position/scrollable-containing-block-validity.html [ Pass ]
 # Regressions with the flag enabled.
-crbug.com/438515315 virtual/css-anchor-update-enabled/external/wpt/css/css-anchor-position/anchor-scroll-position-try-011.html [ Failure ]
 crbug.com/438515315 virtual/css-anchor-update-enabled/external/wpt/css/css-anchor-position/position-area-scrolling-006.html [ Failure ]
 crbug.com/438515315 virtual/css-anchor-update-enabled/external/wpt/css/css-anchor-position/position-area-scrolling-007.html [ Failure ]
 crbug.com/438515315 virtual/css-anchor-update-enabled/external/wpt/css/css-anchor-position/position-try-fallbacks-003.html [ Failure ]
@@ -1124,8 +1123,6 @@
 crbug.com/1076027 external/wpt/css/css-grid/masonry/tentative/track-sizing/auto-repeat/intrinsic-auto-repeat/row-auto-repeat-auto-006.html [ Failure ]
 # Masonry named line failures
 crbug.com/1076027 external/wpt/css/css-grid/masonry/tentative/grid-placement/masonry-grid-placement-named-lines-002.html [ Failure ]
-# Timing out on Windows
-crbug.com/1076027 [ Win ] external/wpt/css/css-grid/masonry/tentative/item-placement/dense-packing/column-dense-packing-multi-span-005.html [ Failure Skip ]
 
 # external/wpt/css/css-fonts/... tests triaged away from the default WPT bug ID
 crbug.com/1211460 external/wpt/css/css-fonts/alternates-order.html [ Failure ]
@@ -4174,6 +4171,8 @@
 crbug.com/347071495 external/wpt/svg/animations/switch-animation-02.html [ Failure ]
 crbug.com/347804255 [ Mac14 ] virtual/force-renderer-accessibility/external/wpt/accessibility/crashtests/slot-assignment-lockup.html [ Crash ]
 crbug.com/347804255 [ Win11-arm64 ] virtual/force-renderer-accessibility/external/wpt/accessibility/crashtests/slot-assignment-lockup.html [ Crash ]
+crbug.com/347055177 external/wpt/geolocation/enabled-by-permissions-policy.https.sub.html [ Failure Timeout ]
+crbug.com/347055177 external/wpt/geolocation/enabled-on-self-origin-by-permissions-policy.https.sub.html [ Failure Timeout ]
 crbug.com/347055177 external/wpt/geolocation/getCurrentPosition_permission_allow.https.html [ Timeout ]
 crbug.com/347055177 external/wpt/geolocation/non-fully-active.https.html [ Timeout ]
 crbug.com/345936780 [ Mac12 ] external/wpt/editing/other/exec-command-with-text-editor.tentative.html?type=text [ Timeout ]
@@ -4495,6 +4494,10 @@
 crbug.com/626703 [ Mac ] external/wpt/url/a-element-xhtml.xhtml?exclude=(file|javascript|mailto) [ Crash Failure ]
 crbug.com/626703 [ Win10.20h2 ] wpt_internal/geolocation-api/watchPosition-page-visibility.https.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/fetch/metadata/generated/element-meta-refresh.optional.sub.html [ Timeout ]
+crbug.com/331556923 external/wpt/geolocation-API/enabled-by-permission-policy-attribute-redirect-on-load.https.sub.html [ Timeout ]
+crbug.com/331556923 external/wpt/geolocation-API/enabled-by-permission-policy-attribute.https.sub.html [ Timeout ]
+crbug.com/331556923 external/wpt/geolocation-API/enabled-by-permissions-policy.https.sub.html [ Timeout ]
+crbug.com/331556923 external/wpt/geolocation-API/enabled-on-self-origin-by-permissions-policy.https.sub.html [ Timeout ]
 crbug.com/626703 [ Mac12 ] external/wpt/webxr/ar-module/xrSession_interactionMode.https.html [ Timeout ]
 crbug.com/626703 [ Mac12 ] external/wpt/webxr/dom-overlay/ar_dom_overlay.https.html [ Timeout ]
 crbug.com/626703 [ Mac13 ] external/wpt/webxr/dom-overlay/ar_dom_overlay.https.html [ Timeout ]
@@ -6783,6 +6786,7 @@
 crbug.com/357649033 virtual/customizable-select-in-page-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select-in-page/customizable-select-in-page-mouse-behavior.tentative.html [ Failure ]
 crbug.com/357649033 virtual/customizable-select-in-page-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select-in-page/select-in-page-typeahead.tentative.html [ Failure ]
 crbug.com/357649033 virtual/customizable-select-in-page-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select-multiple-popup/select-multiple-popup-appearance.tentative.html [ Failure ]
+crbug.com/357649033 virtual/customizable-select-in-page-disabled/external/wpt/html/semantics/forms/the-select-element/customizable-select-in-page/customizable-select-in-page-sizing.tentative.html [ Failure ]
 
 # The height of the appearance:auto <select> button on mac is different from the reference.
 crbug.com/440471997 [ Mac ] wpt_internal/html/semantics/forms/the-select-element/select-multiple-popup-auto-appearance.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 4263bbc..c9f7cc3 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -4749,6 +4749,8 @@
       "external/wpt/html/webappapis/scripting/events/event-handler-attributes-body-window.html",
       "external/wpt/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html",
       "external/wpt/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html",
+      "external/wpt/navigation-api/ordering-and-transition/back-cross-document-event-order.html",
+      "external/wpt/navigation-api/ordering-and-transition/navigate-cross-document-event-order.html",
       "external/wpt/navigation-timing/nested-unload-timing.html",
       "external/wpt/permissions-policy/experimental-features/unload-allowed-by-default.tentative.window.js",
       "external/wpt/permissions-policy/experimental-features/unload-disallowed-subframe.tentative.window.js",
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-scroll-position-try-011.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-scroll-position-try-011.html
index 8bc4fec..6a50dc51 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-scroll-position-try-011.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-scroll-position-try-011.html
@@ -49,6 +49,7 @@
   position-try-fallbacks: --pf1, --pf2, --pf3;
   bottom: anchor(top);
   left: anchor(right);
+  position-visibility: always;
 }
 
 @position-try --pf1 {
@@ -85,9 +86,12 @@
   assert_fallback_position(anchored, anchor, 'left');
 }, 'Should use the last fallback position initially');
 
+const sw = scroller.scrollWidth;
+const sh = scroller.scrollHeight;
+
 promise_test(async () => {
   // Scroll up to have enough space above the anchor, but not enough right.
-  scroller.scrollTop = -250;
+  scroller.scrollTop = -sh;
   scroller.scrollLeft = 150;
   await waitUntilNextAnimationFrame();
   assert_fallback_position(anchored, anchor, 'top');
@@ -97,7 +101,7 @@
 promise_test(async () => {
   // Scroll right to have enough space right to the anchor, but not enough above.
   scroller.scrollTop = -150;
-  scroller.scrollLeft = 250;
+  scroller.scrollLeft = sw;
   await waitUntilNextAnimationFrame();
   assert_fallback_position(anchored, anchor, 'bottom');
   assert_fallback_position(anchored, anchor, 'right');
@@ -105,8 +109,8 @@
 
 promise_test(async () => {
   // Scroll up and right to have enough space on both axes.
-  scroller.scrollTop = -350;
-  scroller.scrollLeft = 250;
+  scroller.scrollTop = -sh;
+  scroller.scrollLeft = sw;
   await waitUntilNextAnimationFrame();
   assert_fallback_position(anchored, anchor, 'top');
   assert_fallback_position(anchored, anchor, 'right');
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/multicol/multicol-gap-decorations-021.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/multicol/multicol-gap-decorations-021.html
new file mode 100644
index 0000000..a410558
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/multicol/multicol-gap-decorations-021.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>Row rule and single-column mulitcol</title>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/#gap-decoration-shorthands">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:1/20px; width:100px; height:100px; gap:10px; row-rule:10px solid green; background:red;">
+  <div style="height:70px; background:green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-020-ref.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-020-ref.html
new file mode 100644
index 0000000..53e9ff3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-020-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  .bar {
+    width: 100px;
+    height: 25px;
+    background: blue;
+  }
+</style>
+<p>There should be three identical blue bars below.</p>
+<div class="bar"></div>
+<div class="bar" style="margin:10px 0;"></div>
+<div class="bar"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-020.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-020.html
new file mode 100644
index 0000000..9534f84
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-020.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>Column rules before and after spanner with margins</title>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-multicol/#column-gaps-and-rules">
+<link rel="help" href="https://drafts.csswg.org/css-multicol/#column-span">
+<link rel="match" href="multicol-span-all-020-ref.html">
+<p>There should be three identical blue bars below.</p>
+<div style="columns:2; width:100px; column-gap:20px; column-rule:20px solid blue;">
+  <div style="height:50px; background:blue;"></div>
+  <div style="column-span:all; margin:10px 0; height:25px; background:blue;"></div>
+  <div style="height:50px; background:blue;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-preserve3d-013.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-preserve3d-013.html
index 224a9e7c..e01b977 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-preserve3d-013.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-preserve3d-013.html
@@ -9,7 +9,7 @@
     'hidden'.  (Note that the ref is nontrivial, because the scrollbar has to
     be scaled appropriately.)">
     <link rel="match" href="transform3d-preserve3d-013-ref.html">
-    <meta name=fuzzy content="maxDifference=0-100;totalPixels=0-3422">
+    <meta name=fuzzy content="maxDifference=0-120;totalPixels=0-3422">
   </head>
   <body>
     <div style="transform: rotatex(45deg); transform-origin: top;
diff --git a/third_party/blink/web_tests/external/wpt/geolocation/enabled-by-permissions-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/geolocation/enabled-by-permissions-policy.https.sub.html
index cf79e491..332e4ce 100644
--- a/third_party/blink/web_tests/external/wpt/geolocation/enabled-by-permissions-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/geolocation/enabled-by-permissions-policy.https.sub.html
@@ -1,9 +1,9 @@
 <!DOCTYPE html>
-<meta charset="utf-8">
+<meta charset="utf-8" />
 <body>
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
-  <script src="/resources/testdriver.js?feature=bidi"></script>
+  <script src="/resources/testdriver.js"></script>
   <script src="/resources/testdriver-vendor.js"></script>
   <script src="/permissions-policy/resources/permissions-policy.js"></script>
   <script>
@@ -13,22 +13,7 @@
       "https://{{hosts[alt][]}}:{{ports[https][0]}}" + same_origin_src;
 
     promise_setup(async () => {
-      // Grant permission for the top-level document's origin.
-      await test_driver.bidi.permissions.set_permission({
-        descriptor: {
-          name: "geolocation"
-        },
-        state: "granted"
-      });
-
-      // Set a default position.
-      await test_driver.bidi.emulation.set_geolocation_override({
-        coordinates: {
-          latitude: 0,
-          longitude: 0,
-          accuracy: 0
-        }
-      });
+      await test_driver.set_permission({ name: "geolocation" }, "granted");
     });
 
     promise_test(async (test) => {
@@ -53,15 +38,6 @@
     }, "Permissions-Policy header geolocation=* allows same-origin iframes.");
 
     promise_test(async (test) => {
-      // Grant site-level permission for the cross-origin iframe's origin.
-      await test_driver.bidi.permissions.set_permission({
-        descriptor: {
-          name: "geolocation"
-        },
-        state: "granted",
-        origin: cross_origin_src
-      });
-
       await test_feature_availability({
         feature_description: "Geolocation API",
         test,
diff --git a/third_party/blink/web_tests/external/wpt/geolocation/enabled-on-self-origin-by-permissions-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/geolocation/enabled-on-self-origin-by-permissions-policy.https.sub.html
index 9f467dd..5940888 100644
--- a/third_party/blink/web_tests/external/wpt/geolocation/enabled-on-self-origin-by-permissions-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/geolocation/enabled-on-self-origin-by-permissions-policy.https.sub.html
@@ -1,9 +1,8 @@
 <!DOCTYPE html>
-<meta charset="utf-8">
 <body>
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
-  <script src="/resources/testdriver.js?feature=bidi"></script>
+  <script src="/resources/testdriver.js"></script>
   <script src="/resources/testdriver-vendor.js"></script>
   <script src="/permissions-policy/resources/permissions-policy.js"></script>
 
@@ -17,22 +16,7 @@
 
 
     promise_setup(async () => {
-      // Grant site-level permission for the top-level document's origin.
-      await test_driver.bidi.permissions.set_permission({
-        descriptor: {
-          name: "geolocation"
-        },
-        state: "granted"
-      });
-
-      // Set a default position.
-      await test_driver.bidi.emulation.set_geolocation_override({
-        coordinates: {
-          latitude: 0,
-          longitude: 0,
-          accuracy: 0
-        }
-      });
+      await test_driver.set_permission({ name: "geolocation" }, "granted");
     });
 
     promise_test(async (t) => {
@@ -56,16 +40,6 @@
     }, "Permissions-Policy header geolocation=(self) allows same-origin iframes.");
 
     promise_test(async (test) => {
-      // This is to ensure that the rejection is due to permission policy
-      // instead of site-level permission.
-      await test_driver.bidi.permissions.set_permission({
-        descriptor: {
-          name: "geolocation"
-        },
-        state: "granted",
-        origin: cross_origin_src
-      });
-
       await test_feature_availability({
         feature_description: "Geolocation API",
         test,
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-global.any.js b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-global.any.js
new file mode 100644
index 0000000..e20aafb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-global.any.js
@@ -0,0 +1,10 @@
+// META: title=`Origin.from(WindowOrWorkerGlobalScope)`
+// META: global=window,worker
+// META: script=/common/get-host-info.sub.js
+
+test(t => {
+  const origin = Origin.from(globalThis);
+  assert_true(!!origin);
+  assert_false(origin.opaque, "Origin should not be opaque.");
+  assert_true(origin.isSameOrigin(Origin.from(get_host_info().ORIGIN)));
+}, `Origin.from(globalThis) is a tuple origin.`);
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-htmlhyperlinkelementutils.window.js b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-htmlhyperlinkelementutils.window.js
new file mode 100644
index 0000000..78f856f3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-htmlhyperlinkelementutils.window.js
@@ -0,0 +1,44 @@
+// META: title=`Origin.from(HTMLHyperlinkElementUtils)`
+// META: script=resources/serializations.js
+
+for (const opaque of urls.opaque) {
+  // <a>
+  test(t => {
+    const a = document.createElement("a");
+    a.href = opaque;
+    const origin = Origin.from(a);
+    assert_true(!!origin);
+    assert_true(origin.opaque);
+  }, `Origin.from(<a href="${opaque}">) returns an opaque origin.`);
+
+  // <area>
+  test(t => {
+    const area = document.createElement("area");
+    area.href = opaque;
+    const origin = Origin.from(area);
+    assert_true(!!origin);
+    assert_true(origin.opaque);
+  }, `Origin.from(<area href="${opaque}">) returns an opaque origin.`);
+}
+
+for (const tuple of urls.tuple) {
+  // <a>
+  test(t => {
+    const a = document.createElement("a");
+    a.href = tuple;
+    const origin = Origin.from(a);
+    assert_true(!!origin);
+    assert_false(origin.opaque);
+  }, `Origin.from(<a href="${tuple}">) returns a tuple origin.`);
+
+  // <area>
+  test(t => {
+    const area = document.createElement("area");
+    area.href = tuple;
+    const origin = Origin.from(area);
+    assert_true(!!origin);
+    assert_false(origin.opaque);
+  }, `Origin.from(<area href="${tuple}">) returns a tuple origin.`);
+}
+
+
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-location.window.js b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-location.window.js
new file mode 100644
index 0000000..9c42d45
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-location.window.js
@@ -0,0 +1,53 @@
+// META: title=`Origin.from(Location)`
+// META: script=/common/get-host-info.sub.js
+
+test(t => {
+  const origin = Origin.from(window.location);
+  assert_true(!!origin);
+  assert_false(origin.opaque);
+  assert_true(origin.isSameOrigin(Origin.from(get_host_info().ORIGIN)));
+}, `Origin.from(window.location) returns a tuple origin.`);
+
+async_test(t => {
+  const el = document.createElement('iframe');
+  el.src = "/common/blank.html";
+  el.onload = t.step_func_done(_ => {
+    const origin = Origin.from(el.contentWindow.location);
+    assert_true(!!origin);
+    assert_false(origin.opaque);
+    assert_true(origin.isSameOrigin(Origin.from(get_host_info().ORIGIN)));
+  });
+  document.body.appendChild(el);
+}, `Origin.from(Location) returns a tuple origin for same-origin frames.`);
+
+async_test(t => {
+  const el = document.createElement('iframe');
+  el.src = get_host_info().REMOTE_ORIGIN + "/common/blank.html";
+  el.onload = t.step_func_done(_ => {
+    assert_throws_js(TypeError, _ => Origin.from(el.contentWindow.location));
+  });
+  document.body.appendChild(el);
+}, `Origin.from(Location) throws for cross-origin frames.`);
+
+async_test(t => {
+  const w = window.open("/html/browsers/windows/resources/post-to-opener.html");
+  window.addEventListener("message", t.step_func(e => {
+    if (e.source === w) {
+      const origin = Origin.from(w.location);
+      assert_true(!!origin);
+      assert_false(origin.opaque);
+      assert_true(origin.isSameOrigin(Origin.from(get_host_info().ORIGIN)));
+      t.done();
+    }
+  }));
+}, `Origin.from(Location) returns a tuple origin for same-origin windows.`);
+
+async_test(t => {
+  const w = window.open(get_host_info().REMOTE_ORIGIN + "/html/browsers/windows/resources/post-to-opener.html");
+  window.addEventListener("message", t.step_func(e => {
+    if (e.source === w) {
+      assert_throws_js(TypeError, _ => Origin.from(w.location));
+      t.done();
+    }
+  }));
+}, `Origin.from(Location) throws for cross-origin windows.`);
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-messageevent.window.js b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-messageevent.window.js
new file mode 100644
index 0000000..c2e225b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-messageevent.window.js
@@ -0,0 +1,74 @@
+// META: title=`Origin.from(MessageEvent)`
+// META: script=/common/get-host-info.sub.js
+
+async_test(t => {
+  const el = document.createElement('iframe');
+  el.src = "/html/browsers/windows/resources/message-parent.html"
+  window.addEventListener("message", t.step_func(e => {
+    if (e.source === el.contentWindow) {
+      const origin = Origin.from(e);
+      assert_true(!!origin);
+      assert_false(origin.opaque);
+      assert_true(origin.isSameOrigin(Origin.from(get_host_info().ORIGIN)));
+      t.done();
+    }
+  }));
+  document.body.appendChild(el);
+}, `Origin.from(MessageEvent) returns a tuple origin for messages from same-origin frames.`);
+
+async_test(t => {
+  const el = document.createElement('iframe');
+  el.src = get_host_info().REMOTE_ORIGIN + "/html/browsers/windows/resources/message-parent.html"
+  window.addEventListener("message", t.step_func(e => {
+    if (e.source === el.contentWindow) {
+      const origin = Origin.from(e);
+      assert_true(!!origin);
+      assert_false(origin.opaque);
+      assert_true(origin.isSameOrigin(Origin.from(get_host_info().REMOTE_ORIGIN)));
+      t.done();
+    }
+  }));
+  document.body.appendChild(el);
+}, `Origin.from(MessageEvent) returns a tuple origin for messages from cross-origin frames.`);
+
+async_test(t => {
+  const el = document.createElement('iframe');
+  el.src = get_host_info().REMOTE_ORIGIN + "/html/browsers/windows/resources/message-parent.html"
+  el.sandbox = "allow-scripts";
+  window.addEventListener("message", t.step_func(e => {
+    if (e.source === el.contentWindow) {
+      const origin = Origin.from(e);
+      assert_true(!!origin);
+      assert_true(origin.opaque);
+      assert_false(origin.isSameOrigin(Origin.from(get_host_info().REMOTE_ORIGIN)));
+      t.done();
+    }
+  }));
+  document.body.appendChild(el);
+}, `Origin.from(MessageEvent) returns an opaque origin for messages from sandboxed frames.`);
+
+async_test(t => {
+  const w = window.open("/html/browsers/windows/resources/post-to-opener.html");
+  window.addEventListener("message", t.step_func(e => {
+    if (e.source === w) {
+      const origin = Origin.from(e);
+      assert_true(!!origin);
+      assert_false(origin.opaque);
+      assert_true(origin.isSameOrigin(Origin.from(get_host_info().ORIGIN)));
+      t.done();
+    }
+  }));
+}, `Origin.from(MessageEvent) returns a tuple origin for same-origin windows.`);
+
+async_test(t => {
+  const w = window.open(get_host_info().REMOTE_ORIGIN + "/html/browsers/windows/resources/post-to-opener.html");
+  window.addEventListener("message", t.step_func(e => {
+    if (e.source === w) {
+      const origin = Origin.from(e);
+      assert_true(!!origin);
+      assert_false(origin.opaque);
+      assert_true(origin.isSameOrigin(Origin.from(get_host_info().REMOTE_ORIGIN)));
+      t.done();
+    }
+  }));
+}, `Origin.from(MessageEvent) returns a tuple origin for cross-origin windows.`);
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-origin.any.js b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-origin.any.js
new file mode 100644
index 0000000..c167075
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-origin.any.js
@@ -0,0 +1,22 @@
+// META: title=`Origin.from(URL)`
+// META: script=resources/serializations.js
+
+for (const opaque of urls.opaque) {
+  test(t => {
+    const originFromString = Origin.from(opaque);
+    const origin = Origin.from(originFromString);
+    assert_true(!!origin);
+    assert_true(origin.opaque, "Origin should be opaque.");
+    assert_true(origin.isSameOrigin(originFromString));
+  }, `Origin.from(Origin.from(${JSON.stringify(opaque)})) is an opaque origin.`);
+}
+
+for (const tuple of urls.tuple) {
+  test(t => {
+    const originFromString = Origin.from(tuple);
+    const origin = Origin.from(originFromString);
+    assert_true(!!origin);
+    assert_false(origin.opaque, "Origin should be opaque.");
+    assert_true(origin.isSameOrigin(originFromString));
+  }, `Origin.from(Origin.from(${JSON.stringify(tuple)})) is an tuple origin.`);
+}
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-string.any.js b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-string.any.js
new file mode 100644
index 0000000..5cf47cc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-string.any.js
@@ -0,0 +1,24 @@
+// META: title=`Origin.from(String)`
+// META: script=resources/serializations.js
+
+for (const invalid of urls.invalid) {
+  test(t => {
+    assert_throws_js(TypeError, _ => Origin.from(invalid));
+  }, `Origin.from(${JSON.stringify(invalid)}) throws a TypeError.`);
+}
+
+for (const opaque of urls.opaque) {
+  test(t => {
+    const origin = Origin.from(opaque);
+    assert_true(!!origin);
+    assert_true(origin.opaque, "Origin should be opaque.");
+  }, `Origin.from(${JSON.stringify(opaque)}) is an opaque origin.`);
+}
+
+for (const tuple of urls.tuple) {
+  test(t => {
+    const origin = Origin.from(tuple);
+    assert_true(!!origin);
+    assert_false(origin.opaque, "Origin should not be opaque.");
+  }, `Origin.from(${JSON.stringify(tuple)}) is an opaque origin.`);
+}
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-url.any.js b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-url.any.js
new file mode 100644
index 0000000..e8a128e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-url.any.js
@@ -0,0 +1,24 @@
+// META: title=`Origin.from(URL)`
+// META: script=resources/serializations.js
+
+for (const invalid of urls.invalid) {
+  test(t => {
+    assert_throws_js(TypeError, _ => Origin.from(new URL(invalid)));
+  }, `Origin.from(${JSON.stringify(invalid)}) throws a TypeError.`);
+}
+
+for (const opaque of urls.opaque) {
+  test(t => {
+    const origin = Origin.from(new URL(opaque));
+    assert_true(!!origin);
+    assert_true(origin.opaque, "Origin should be opaque.");
+  }, `Origin.from(${JSON.stringify(opaque)}) is an opaque origin.`);
+}
+
+for (const tuple of urls.tuple) {
+  test(t => {
+    const origin = Origin.from(new URL(tuple));
+    assert_true(!!origin);
+    assert_false(origin.opaque, "Origin should not be opaque.");
+  }, `Origin.from(${JSON.stringify(tuple)}) is an opaque origin.`);
+}
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-window.window.js b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-window.window.js
new file mode 100644
index 0000000..78543db
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from-window.window.js
@@ -0,0 +1,64 @@
+// META: title=`Origin.from(Location)`
+// META: script=/common/get-host-info.sub.js
+
+test(t => {
+  const origin = Origin.from(window);
+  assert_true(!!origin);
+  assert_false(origin.opaque);
+  assert_true(origin.isSameOrigin(Origin.from(get_host_info().ORIGIN)));
+}, `Origin.from(window) returns a tuple origin.`);
+
+async_test(t => {
+  const el = document.createElement('iframe');
+  el.src = "/common/blank.html";
+  el.onload = t.step_func_done(_ => {
+    const origin = Origin.from(el.contentWindow);
+    assert_true(!!origin);
+    assert_false(origin.opaque);
+    assert_true(origin.isSameOrigin(Origin.from(get_host_info().ORIGIN)));
+  });
+  document.body.appendChild(el);
+}, `Origin.from(Window) returns a tuple origin for same-origin frames.`);
+
+async_test(t => {
+  const el = document.createElement('iframe');
+  el.src = get_host_info().REMOTE_ORIGIN + "/common/blank.html";
+  el.onload = t.step_func_done(_ => {
+    assert_throws_js(TypeError, _ => Origin.from(el.contentWindow));
+  });
+  document.body.appendChild(el);
+}, `Origin.from(Window) throws for cross-origin frames.`);
+
+async_test(t => {
+  const el = document.createElement('iframe');
+  el.src = "/common/blank.html";
+  el.sandbox = "allow-scripts";
+  el.onload = t.step_func_done(_ => {
+    assert_throws_js(TypeError, _ => Origin.from(el.contentWindow));
+    t.done();
+  });
+  document.body.appendChild(el);
+}, `Origin.from(Window) throws for sandboxed frames.`);
+
+async_test(t => {
+  const w = window.open("/html/browsers/windows/resources/post-to-opener.html");
+  window.addEventListener("message", t.step_func(e => {
+    if (e.source === w) {
+      const origin = Origin.from(w);
+      assert_true(!!origin);
+      assert_false(origin.opaque);
+      assert_true(origin.isSameOrigin(Origin.from(get_host_info().ORIGIN)));
+      t.done();
+    }
+  }));
+}, `Origin.from(Window) returns a tuple origin for same-origin windows.`);
+
+async_test(t => {
+  const w = window.open(get_host_info().REMOTE_ORIGIN + "/html/browsers/windows/resources/post-to-opener.html");
+  window.addEventListener("message", t.step_func(e => {
+    if (e.source === w) {
+      assert_throws_js(TypeError, _ => Origin.from(w));
+      t.done();
+    }
+  }));
+}, `Origin.from(Window) throws for cross-origin windows.`);
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from.any.js b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from.any.js
new file mode 100644
index 0000000..fcbc993a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/origin-from.any.js
@@ -0,0 +1,25 @@
+// META: title=`Origin.from()`
+// META: script=resources/serializations.js
+
+//
+// Invalid Inputs: `null`, `undefined`, invalid URL strings, random objects.
+//
+const invalidInputs = [
+  null,
+  undefined,
+  1,
+  1.1,
+  true,
+  {},
+  Object,
+  Origin,
+  Origin.from,
+];
+
+for (const invalid of invalidInputs) {
+  test(t => {
+    assert_throws_js(TypeError, _ => Origin.from(invalid));
+  }, `Origin.from(${invalid}) throws a TypeError.`);
+}
+
+// Specific object types are tested in `origin-from-*.js` in this directory.
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/resources/serializations.js b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/resources/serializations.js
new file mode 100644
index 0000000..dec7fbd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/origin/tentative/api/resources/serializations.js
@@ -0,0 +1,36 @@
+const urls = {
+  invalid: [
+    "",
+    "not-valid",
+  ],
+  opaque: [
+    "about:blank",
+    "data:text/plain,opaque",
+    "weird-protocol:whatever",
+    "weird-hierarchical-protocol://host/path?etc",
+    "blob:weird-protocol:whatever",
+    "blob:weird-hierarchical-protocol://host/path?etc",
+  ],
+  tuple: [
+    "http://site.example",
+    "https://site.example",
+    "https://site.example:123",
+    "http://sub.site.example",
+    "https://sub.site.example",
+    "https://sub.site.example:123",
+    "https://xn--mlauted-m2a.example",
+    "ftp://ftp.example",
+    "ws://ws.example",
+    "wss://wss.example",
+    "https://trailing.slash/",
+    "https://user:pass@site.example",
+    "https://has.a.port:1234/and/path",
+    "https://ümlauted.example",
+    "file:///path/to/a/file.txt",
+    "blob:https://example.com/some-guid",
+    "ftp://example.com/",
+    "https://example.com/path?query#fragment",
+    "https://127.0.0.1/",
+    "https://[::1]/",
+  ],
+};
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select-in-page/customizable-select-in-page-sizing-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select-in-page/customizable-select-in-page-sizing-ref.html
new file mode 100644
index 0000000..7954fc0c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select-in-page/customizable-select-in-page-sizing-ref.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<link rel=stylesheet href="resources/customizable-select-in-page.css">
+<link rel=stylesheet href="../customizable-select/resources/customizable-select-styles.css">
+
+<style>
+#size4 {
+  height: calc(24px * 4);
+}
+#size2 {
+  height: calc(24px * 2);
+}
+#big2 {
+  font-size: 26px;
+  height: 2lh;
+}
+</style>
+
+<div class=customizable-select-in-page id=size4>
+  <div class="customizable-select-option selected">one</div>
+  <div class=customizable-select-option>two</div>
+  <div class=customizable-select-option>three</div>
+  <div class=customizable-select-option>four</div>
+</div>
+
+<div class=customizable-select-in-page id=size2>
+  <div class="customizable-select-option selected">one</div>
+  <div class=customizable-select-option>two</div>
+  <div class=customizable-select-option>three</div>
+  <div class=customizable-select-option>four</div>
+</div>
+
+<div class=customizable-select-in-page id=big2>
+  <div class="customizable-select-option selected">big one</div>
+  <div class=customizable-select-option>big two</div>
+  <div class=customizable-select-option>big three</div>
+  <div class=customizable-select-option>big four</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select-in-page/customizable-select-in-page-sizing.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select-in-page/customizable-select-in-page-sizing.tentative.html
new file mode 100644
index 0000000..44d0939
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select-in-page/customizable-select-in-page-sizing.tentative.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/w3c/csswg-drafts/issues/12510#issuecomment-3361831625">
+<link rel=match href="customizable-select-in-page-sizing-ref.html">
+
+<style>
+select {
+  appearance: base-select;
+}
+#big {
+  font-size: 26px;
+}
+</style>
+
+<select multiple>
+  <option selected>one</option>
+  <option>two</option>
+  <option>three</option>
+  <option>four</option>
+</select>
+
+<select size=2>
+  <option selected>one</option>
+  <option>two</option>
+  <option>three</option>
+  <option>four</option>
+</select>
+
+<select size=2 id=big>
+  <option selected>big one</option>
+  <option>big two</option>
+  <option>big three</option>
+  <option>big four</option>
+</select>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select-in-page/resources/customizable-select-in-page.css b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select-in-page/resources/customizable-select-in-page.css
index ec2c9c0..e1b5eb5 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select-in-page/resources/customizable-select-in-page.css
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select-in-page/resources/customizable-select-in-page.css
@@ -3,6 +3,7 @@
   overflow-inline: auto;
   overflow-block: auto;
   border: 1px solid;
+  block-size: calc(max(24px, 1lh) * attr(size type(<integer>), 4));
 }
 
 .customizable-select-in-page.disabled {
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-keyboard-behavior.optional.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-keyboard-behavior.optional.html
index 6355823..f59b20d 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-keyboard-behavior.optional.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-keyboard-behavior.optional.html
@@ -126,12 +126,6 @@
 
   promise_test(async t => {
     addCloseCleanup(t);
-
-    await test_driver.send_keys(select, Enter);
-    await new Promise(requestAnimationFrame);
-    assert_false(select.matches(':open'),
-      'Enter should not open the listbox when outside a form.');
-
     form.appendChild(select);
     let formWasSubmitted = false;
     form.addEventListener('submit', event => {
@@ -141,10 +135,15 @@
     await test_driver.send_keys(select, Enter);
     await new Promise(requestAnimationFrame);
     assert_false(formWasSubmitted,
-      'Enter should submit the form when the listbox is closed.');
-    assert_false(select.matches(':open'),
-      'Enter should not open the listbox when it is in a form.');
-  }, `${id}: When the listbox is closed, the enter key should submit the form or do nothing.`);
+      'Enter should not submit the form when the listbox is closed.');
+    if (navigator.platform.startsWith('Mac')) {
+      assert_false(select.matches(':open'),
+        'Enter should not open the listbox on Mac platforms.');
+    } else {
+      assert_true(select.matches(':open'),
+        'Enter should open the listbox on non-Mac platforms.');
+    }
+  }, `${id}: When the listbox is closed, the enter key should not trigger form submission.`);
 
   promise_test(async t => {
     addCloseCleanup(t);
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/invalid-css-properties.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/invalid-css-properties.tentative.html
index f8dd5b6..877eefa 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/invalid-css-properties.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/invalid-css-properties.tentative.html
@@ -18,6 +18,7 @@
     transform: rotate(10);
     border: 0px;
     cursor: none;
+    content-visibility: hidden;
   }
 </style>
 
@@ -34,6 +35,7 @@
     assert_equals(getComputedStyle(el_with_negatives).paddingLeft, "0px", "padding-left");
     assert_equals(getComputedStyle(el_with_negatives).transform, "none", "transform");
     assert_equals(getComputedStyle(el_with_negatives).cursor, "pointer", "cursor");
+    assert_equals(getComputedStyle(el_with_negatives).contentVisibility, "visible", "content-visibility");
   }, "None of the listed properties should be applied");
 </script>
 </body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/webkit-text-security-on-parent-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/webkit-text-security-on-parent-ref.html
new file mode 100644
index 0000000..4d44be0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/webkit-text-security-on-parent-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Reference for Permission Element with -webkit-text-security on parent</title>
+
+<div id="container"><permission id="p" type="camera"></permission></div>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/webkit-text-security-on-parent.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/webkit-text-security-on-parent.tentative.html
new file mode 100644
index 0000000..5b8d7e2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/webkit-text-security-on-parent.tentative.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Permission Element with -webkit-text-security on parent</title>
+<link rel="help" href="https://github.com/WICG/PEPC">
+<link rel="match" href="webkit-text-security-on-parent-ref.html">
+<style>
+  #container {
+    -webkit-text-security: disc;
+  }
+</style>
+
+<div id="container"><permission id="p" type="camera"></permission></div>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-focus-slotted.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-focus-slotted.html
new file mode 100644
index 0000000..db46d139
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-focus-slotted.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover focus with slotted popover and invoker</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://crbug.com/447888734">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/html/semantics/popovers/resources/popover-utils.js"></script>
+
+<div id="host">
+  <template shadowrootmode="open">
+    <div>
+      <slot id="invoker" name="invoker"></slot>
+      <slot id="popover" popover="manual"></slot>
+    </div>
+  </template>
+  <button slot="invoker">Click me</button>
+  <button id="inner">Click me next</button>
+</div>
+
+<script>
+  const div = document.getElementById('host');
+  const invoker = div.shadowRoot.querySelector("#invoker");
+  const popover = div.shadowRoot.querySelector("#popover");
+  const inner = document.getElementById('inner');
+  popover.togglePopover({source: invoker});
+
+  promise_test(async () => {
+    assert_true(popover.matches(':popover-open'), 'Popover should be open');
+    inner.focus();
+    assert_equals(document.activeElement, inner, 'Start with inner focused');
+
+    // Tab forward
+    await sendTab();
+    assert_not_equals(document.activeElement, inner, 'Focus should move');
+  }, 'Tabbing forward out of a <slot popover> should not hang.');
+
+  promise_test(async () => {
+    assert_true(popover.matches(':popover-open'), 'Popover should be open');
+    inner.focus();
+    assert_equals(document.activeElement, inner, 'Start with inner focused');
+
+    // Tab backwards
+    await sendShiftTab();
+    assert_not_equals(document.activeElement, inner, 'Focus should move');
+  }, 'Tabbing backwards out of a <slot popover> should not hang.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-addAnimation.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-addAnimation.tentative.html
index 721a2a5..eaa9747 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-addAnimation.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-addAnimation.tentative.html
@@ -65,7 +65,6 @@
       function setupAnimationTrigger(use_default_trigger=false) {
         const trigger = use_default_trigger ? new TimelineTrigger()
             : new TimelineTrigger({
-              behavior: "alternate",
               timeline: view_timeline,
               rangeStart: `${TRIGGER_START_PX}px`,
               rangeEnd: `${TRIGGER_END_PX}px`
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-alternate.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-alternate.tentative.html
index 78043964..e676b8d 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-alternate.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-alternate.tentative.html
@@ -26,12 +26,12 @@
       #scroll_target {
         animation-trigger:
              trigger(--scrolltrigger, enter play-forwards, exit play-backwards);
-        timeline-trigger: --scrolltrigger scroll() alternate 150px 200px;
+        timeline-trigger: --scrolltrigger scroll() 150px 200px;
       }
       #view_target {
         animation-trigger:
                trigger(--viewtrigger, enter play-forwards, exit play-backwards);
-        timeline-trigger: --viewtrigger view() alternate 150px 200px;
+        timeline-trigger: --viewtrigger view() 150px 200px;
       }
       #deferred_target {
         animation-trigger:
@@ -39,7 +39,7 @@
       }
       #deferred_subject {
         view-timeline: --viewtimeline;
-        timeline-trigger: --deferredtrigger --viewtimeline alternate 150px 200px;
+        timeline-trigger: --deferredtrigger --viewtimeline 150px 200px;
       }
 
       .scroller {
@@ -135,7 +135,7 @@
                                                            CSS_TRIGGER_END_PX,
                                                            scroller);
         await testAlternateAnimationTrigger(test, rangeBoundaries);
-      }, "once animation triggered via scroll() timeline.");
+      }, "animation triggered via scroll() timeline.");
 
       promise_test(async (test) => {
         scroller = view_scroller;
@@ -150,7 +150,7 @@
                                       COVER_START_OFFSET + CSS_TRIGGER_END_PX,
                                       scroller);
         await testAlternateAnimationTrigger(test, rangeBoundaries);
-      }, "once animation triggered via view() timeline.");
+      }, "animation triggered via view() timeline.");
 
       promise_test(async (test) => {
         scroller = deferred_scroller;
@@ -165,7 +165,7 @@
                                       COVER_START_OFFSET + CSS_TRIGGER_END_PX,
                                       scroller);
         await testAlternateAnimationTrigger(test, rangeBoundaries);
-      }, "once animation triggered via deferred (view) timeline.");
+      }, "animation triggered via deferred (view) timeline.");
     </script>
   </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-disarmed-by-apis.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-disarmed-by-apis.tentative.html
index 5ca46f88..990ca47 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-disarmed-by-apis.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-disarmed-by-apis.tentative.html
@@ -93,7 +93,6 @@
       }
       function setupAnimationTrigger(use_default_trigger=false) {
         const trigger = new TimelineTrigger({
-              behavior: "alternate",
               timeline: view_timeline,
               rangeStart: `${TRIGGER_START_PX}px`,
               rangeEnd: `${TRIGGER_END_PX}px`
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-fill-mode-both.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-fill-mode-both.tentative.html
index da583db..c2b41732 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-fill-mode-both.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-fill-mode-both.tentative.html
@@ -24,7 +24,7 @@
         width: 100%;
         background-color: blue;
         animation: slide-in 0.3s both;
-        timeline-trigger: --trigger view() alternate contain 0% contain 100%;
+        timeline-trigger: --trigger view() contain 0% contain 100%;
         animation-trigger: trigger(--trigger, enter play-forwards, exit play-backwards);
         position: absolute;
       }
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-fill-mode-none.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-fill-mode-none.tentative.html
index 02aa7f5..bf90373a 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-fill-mode-none.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-fill-mode-none.tentative.html
@@ -23,7 +23,7 @@
         width: 100%;
         background-color: blue;
         animation: slide-in 3s none;
-        timeline-trigger: --trigger view() alternate contain 0% contain 100%;
+        timeline-trigger: --trigger view() contain 0% contain 100%;
         animation-trigger: trigger(--trigger, enter play-forwards, exit play-backwards);
       }
       .space {
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-getanimations.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-getanimations.tentative.html
index c246ec10..7f971e2 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-getanimations.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-getanimations.tentative.html
@@ -25,7 +25,7 @@
         width: 100%;
         background-color: blue;
         animation: slide-in 3s;
-        timeline-trigger: --trigger view() alternate contain 0% contain 100%;
+        timeline-trigger: --trigger view() contain 0% contain 100%;
         animation-trigger: --trigger;
       }
       .target {
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-late-attached-timeline.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-late-attached-timeline.tentative.html
index 7b5317aa..9d111dfe 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-late-attached-timeline.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-late-attached-timeline.tentative.html
@@ -21,7 +21,7 @@
       }
       .target {
         animation: myAnim linear 0.5s forwards;
-        timeline-trigger: --trigger once 150px 200px;
+        timeline-trigger: --trigger 150px 200px;
       }
       .deferred.subject {
         view-timeline: --viewtimeline;
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-multiple-animations.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-multiple-animations.tentative.html
index 95ecce4..80275a40 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-multiple-animations.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-multiple-animations.tentative.html
@@ -70,7 +70,6 @@
       const view_timeline = new ViewTimeline({ subject: subject });
       function setupAnimationTrigger() {
         const trigger = new TimelineTrigger({
-              behavior: "alternate",
               timeline: view_timeline,
               rangeStart: `${TRIGGER_START_PX}px`,
               rangeEnd: `${TRIGGER_END_PX}px`
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-multiple-triggers.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-multiple-triggers.tentative.html
index fa493f17..090f80cc 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-multiple-triggers.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-multiple-triggers.tentative.html
@@ -78,7 +78,6 @@
       }
       function setupAnimationTrigger(view_timeline) {
         const trigger = new TimelineTrigger({
-              behavior: "repeat",
               timeline: view_timeline,
               rangeStart: `${TRIGGER_START_PX}px`,
               rangeEnd: `${TRIGGER_END_PX}px`
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-once-play-state.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-once-play-state.tentative.html
index 42887c8a..4683b90 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-once-play-state.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-once-play-state.tentative.html
@@ -24,7 +24,7 @@
       }
       .target {
         animation: myAnim linear 0.5s forwards;
-        timeline-trigger: --trigger once --viewtimeline 150px 200px;
+        timeline-trigger: --trigger --viewtimeline 150px 200px;
         animation-trigger: trigger(--trigger, enter play-once);
       }
       .scroller {
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-range-px-roundtrip.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-range-px-roundtrip.tentative.html
index 9776a6a..b4de12d 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-range-px-roundtrip.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-range-px-roundtrip.tentative.html
@@ -12,7 +12,6 @@
       // 1, i.e. the ratio of physical pixels to CSS pixels isn't 1.
       promise_test(async (test) => {
         let trigger = new TimelineTrigger({
-          behavior: "alternate",
           timeline: new ViewTimeline({
             subject: document.getElementById('subject'), axis: "y"
           }),
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-repeat.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-repeat.tentative.html
index 4d7d2fe..91e69590 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-repeat.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-repeat.tentative.html
@@ -25,18 +25,18 @@
 
       #scroll_target {
         animation-trigger: trigger(--scrolltrigger, enter play-forwards, exit reset);
-        timeline-trigger: --scrolltrigger scroll() repeat 250px 300px 200px 350px;
+        timeline-trigger: --scrolltrigger scroll() 250px 300px 200px 350px;
       }
       #view_target {
         animation-trigger: trigger(--viewtrigger, enter play-forwards, exit reset);
-        timeline-trigger: --viewtrigger view() repeat 250px 300px 200px 350px;
+        timeline-trigger: --viewtrigger view() 250px 300px 200px 350px;
       }
       #deferred_target {
         animation-trigger: trigger(--deferredtrigger, enter play-forwards, exit reset);
       }
       #deferred_subject {
         view-timeline: --viewtimeline;
-        timeline-trigger: --deferredtrigger --viewtimeline repeat 250px 300px 200px 350px;
+        timeline-trigger: --deferredtrigger --viewtimeline 250px 300px 200px 350px;
       }
 
       .scroller {
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-state.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-state.tentative.html
index 7673a4ca..c0fb224 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-state.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/animation-trigger-state.tentative.html
@@ -25,18 +25,18 @@
 
       #scroll_target {
         animation-trigger: trigger(--scrolltrigger, enter play-once, exit pause);
-        timeline-trigger: --scrolltrigger scroll() state 150px 200px;
+        timeline-trigger: --scrolltrigger scroll() 150px 200px;
       }
       #view_target {
         animation-trigger: trigger(--viewtrigger, enter play-once, exit pause);
-        timeline-trigger: --viewtrigger view() state 150px 200px;
+        timeline-trigger: --viewtrigger view() 150px 200px;
       }
       #deferred_target {
         animation-trigger: trigger(--deferredtrigger, enter play-once, exit pause);
       }
       #deferred_subject {
         view-timeline: --viewtimeline;
-        timeline-trigger: --deferredtrigger --viewtimeline state 150px 200px;
+        timeline-trigger: --deferredtrigger --viewtimeline 150px 200px;
       }
 
       .scroller {
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-behavior-computed.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-behavior-computed.tentative.html
deleted file mode 100644
index 618ac062..0000000
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-behavior-computed.tentative.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8">
-<title>CSS Animations: getComputedStyle().animationTriggerBehavior</title>
-<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
-<meta name="assert" content="timeline-trigger-type computed value is as specified.">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/css/support/computed-testcommon.js"></script>
-</head>
-<body>
-<div id="target"></div>
-<script>
-test_computed_value("timeline-trigger-behavior", "initial", "once");
-test_computed_value("timeline-trigger-behavior", "once, repeat, alternate, state");
-</script>
-</body>
-</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-behavior-valid.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-behavior-valid.tentative.html
deleted file mode 100644
index 7d710049..0000000
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-behavior-valid.tentative.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8">
-<title>CSS Animations: parsing timeline-trigger-behavior with valid values</title>
-<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
-<meta name="assert" content="animation-composition supports the full grammar '<single-timeline-trigger-behavior> #'.">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/css/support/parsing-testcommon.js"></script>
-</head>
-<body>
-<script>
-test_valid_value("timeline-trigger-behavior", "once");
-test_valid_value("timeline-trigger-behavior", "repeat");
-test_valid_value("timeline-trigger-behavior", "alternate");
-test_valid_value("timeline-trigger-behavior", "state");
-test_valid_value("timeline-trigger-behavior", "once, repeat");
-test_valid_value("timeline-trigger-behavior", "once, repeat, alternate");
-test_valid_value("timeline-trigger-behavior", "once, repeat, alternate, state");
-
-test_invalid_value("timeline-trigger-behavior", "oncerepeat");
-test_invalid_value("timeline-trigger-behavior", "repeatalternate");
-test_invalid_value("timeline-trigger-behavior", "once, repeatalternate");
-test_invalid_value("timeline-trigger-behavior", "junk");
-test_invalid_value("timeline-trigger-behavior", "enco, taeper, etanretla, etats");
-</script>
-</body>
-</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-shorthand.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-shorthand.tentative.html
index 51dc361..026dfc1 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-shorthand.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-shorthand.tentative.html
@@ -9,10 +9,9 @@
 <div id="target" style="font-size:10px"></div>
 <script>
   test_shorthand_value('timeline-trigger',
-    '--my-trigger view() once contain 0% contain 100% cover 0% cover 100%',
+    '--my-trigger view() contain 0% contain 100% cover 0% cover 100%',
     {
       'timeline-trigger-name': '--my-trigger',
-      'timeline-trigger-behavior': 'once',
       'timeline-trigger-source': 'view()',
       'timeline-trigger-range-start': 'contain',
       'timeline-trigger-range-end': 'contain',
@@ -20,10 +19,9 @@
       'timeline-trigger-exit-range-end': 'cover',
     });
     test_shorthand_value('timeline-trigger',
-    '--my-trigger view() once contain 20% contain 80% cover 10% cover 90%',
+    '--my-trigger view() contain 20% contain 80% cover 10% cover 90%',
     {
       'timeline-trigger-name': '--my-trigger',
-      'timeline-trigger-behavior': 'once',
       'timeline-trigger-source': 'view()',
       'timeline-trigger-range-start': 'contain 20%',
       'timeline-trigger-range-end': 'contain 80%',
@@ -31,10 +29,9 @@
       'timeline-trigger-exit-range-end': 'cover 90%',
     });
   test_shorthand_value('timeline-trigger',
-    '--my-trigger view() once contain 0% contain 100% cover 0%',
+    '--my-trigger view() contain 0% contain 100% cover 0%',
     {
       'timeline-trigger-name': '--my-trigger',
-      'timeline-trigger-behavior': 'once',
       'timeline-trigger-source': 'view()',
       'timeline-trigger-range-start': 'contain',
       'timeline-trigger-range-end': 'contain',
@@ -42,10 +39,9 @@
       'timeline-trigger-exit-range-end': 'cover',
     });
   test_shorthand_value('timeline-trigger',
-    '--my-trigger view() once contain 20% contain 80% cover 10%',
+    '--my-trigger view() contain 20% contain 80% cover 10%',
     {
       'timeline-trigger-name': '--my-trigger',
-      'timeline-trigger-behavior': 'once',
       'timeline-trigger-source': 'view()',
       'timeline-trigger-range-start': 'contain 20%',
       'timeline-trigger-range-end': 'contain 80%',
@@ -53,10 +49,9 @@
       'timeline-trigger-exit-range-end': 'cover',
     });
   test_shorthand_value('timeline-trigger',
-    '--my-trigger view() once contain 0% contain 100% cover',
+    '--my-trigger view() contain 0% contain 100% cover',
     {
       'timeline-trigger-name': '--my-trigger',
-      'timeline-trigger-behavior': 'once',
       'timeline-trigger-source': 'view()',
       'timeline-trigger-range-start': 'contain',
       'timeline-trigger-range-end': 'contain',
@@ -64,10 +59,9 @@
       'timeline-trigger-exit-range-end': 'cover',
     });
   test_shorthand_value('timeline-trigger',
-    '--my-trigger view() alternate contain 0% contain 100%',
+    '--my-trigger view() contain 0% contain 100%',
     {
       'timeline-trigger-name': '--my-trigger',
-      'timeline-trigger-behavior': 'alternate',
       'timeline-trigger-source': 'view()',
       'timeline-trigger-range-start': 'contain',
       'timeline-trigger-range-end': 'contain',
@@ -75,10 +69,9 @@
       'timeline-trigger-exit-range-end': 'auto',
     });
   test_shorthand_value('timeline-trigger',
-    '--my-trigger view() alternate contain 20% contain 80%',
+    '--my-trigger view() contain 20% contain 80%',
     {
       'timeline-trigger-name': '--my-trigger',
-      'timeline-trigger-behavior': 'alternate',
       'timeline-trigger-source': 'view()',
       'timeline-trigger-range-start': 'contain 20%',
       'timeline-trigger-range-end': 'contain 80%',
@@ -86,10 +79,9 @@
       'timeline-trigger-exit-range-end': 'auto',
     });
   test_shorthand_value('timeline-trigger',
-    '--my-trigger view() alternate contain 0%',
+    '--my-trigger view() contain 0%',
     {
       'timeline-trigger-name': '--my-trigger',
-      'timeline-trigger-behavior': 'alternate',
       'timeline-trigger-source': 'view()',
       'timeline-trigger-range-start': 'contain',
       'timeline-trigger-range-end': 'contain',
@@ -97,10 +89,9 @@
       'timeline-trigger-exit-range-end': 'auto',
     });
   test_shorthand_value('timeline-trigger',
-    '--my-trigger view() alternate contain 10%',
+    '--my-trigger view() contain 10%',
     {
       'timeline-trigger-name': '--my-trigger',
-      'timeline-trigger-behavior': 'alternate',
       'timeline-trigger-source': 'view()',
       'timeline-trigger-range-start': 'contain 10%',
       'timeline-trigger-range-end': 'contain',
@@ -108,40 +99,27 @@
       'timeline-trigger-exit-range-end': 'auto',
     });
   test_shorthand_value('timeline-trigger',
-    '--my-trigger view() alternate contain',
+    '--my-trigger view() contain',
     {
       'timeline-trigger-name': '--my-trigger',
-      'timeline-trigger-behavior': 'alternate',
       'timeline-trigger-source': 'view()',
       'timeline-trigger-range-start': 'contain',
       'timeline-trigger-range-end': 'contain',
       'timeline-trigger-exit-range-start': 'auto',
       'timeline-trigger-exit-range-end': 'auto',
     });
-  test_shorthand_value('timeline-trigger', '--my-trigger view() alternate',
+  test_shorthand_value('timeline-trigger', '--my-trigger view()',
     {
       'timeline-trigger-name': '--my-trigger',
-      'timeline-trigger-behavior': 'alternate',
       'timeline-trigger-source': 'view()',
       'timeline-trigger-range-start': 'normal',
       'timeline-trigger-range-end': 'normal',
       'timeline-trigger-exit-range-start': 'auto',
       'timeline-trigger-exit-range-end': 'auto',
     });
-  test_shorthand_value('timeline-trigger', 'repeat',
-    {
-      'timeline-trigger-name': 'none',
-      'timeline-trigger-behavior': 'repeat',
-      'timeline-trigger-source': 'auto',
-      'timeline-trigger-range-start': 'normal',
-      'timeline-trigger-range-end': 'normal',
-      'timeline-trigger-exit-range-start': 'auto',
-      'timeline-trigger-exit-range-end': 'auto',
-    });
   test_shorthand_value('timeline-trigger', '--my-trigger --my-timeline',
     {
       'timeline-trigger-name': '--my-trigger',
-      'timeline-trigger-behavior': 'once',
       'timeline-trigger-source': '--my-timeline',
       'timeline-trigger-range-start': 'normal',
       'timeline-trigger-range-end': 'normal',
@@ -152,7 +130,6 @@
   test_shorthand_value('timeline-trigger', '--my-trigger1, --my-trigger2',
     {
       'timeline-trigger-name': '--my-trigger1, --my-trigger2',
-      'timeline-trigger-behavior': 'once, once',
       'timeline-trigger-source': 'auto, auto',
       'timeline-trigger-range-start': 'normal, normal',
       'timeline-trigger-range-end': 'normal, normal',
@@ -160,11 +137,10 @@
       'timeline-trigger-exit-range-end': 'auto, auto',
     });
   test_shorthand_value('timeline-trigger',
-  '--my-trigger1 --my-timeline1 once contain cover normal exit, '+
-  '--my-trigger2 --my-timeline2 alternate entry exit contain normal',
+  '--my-trigger1 --my-timeline1 contain cover normal exit, '+
+  '--my-trigger2 --my-timeline2 entry exit contain normal',
     {
       'timeline-trigger-name': '--my-trigger1, --my-trigger2',
-      'timeline-trigger-behavior': 'once, alternate',
       'timeline-trigger-source': '--my-timeline1, --my-timeline2',
       'timeline-trigger-range-start': 'contain, entry',
       'timeline-trigger-range-end': 'cover, exit',
@@ -172,42 +148,37 @@
       'timeline-trigger-exit-range-end': 'exit, normal',
     });
   test_computed_value('timeline-trigger',
-    '--my-trigger view() once contain 0% contain 100% cover 0% cover 100%',
-    '--my-trigger view() once contain contain cover cover');
+    '--my-trigger view() contain 0% contain 100% cover 0% cover 100%',
+    '--my-trigger view() contain contain cover cover');
   test_computed_value('timeline-trigger',
-    '--my-trigger view() once contain 20% contain 80% cover 10% cover 90%');
+    '--my-trigger view() contain 20% contain 80% cover 10% cover 90%');
   test_computed_value('timeline-trigger',
-    '--my-trigger view() once contain 20% contain 80% cover 0%',
-    '--my-trigger view() once contain 20% contain 80% cover cover');
+    '--my-trigger view() contain 20% contain 80% cover 0%',
+    '--my-trigger view() contain 20% contain 80% cover cover');
   test_computed_value('timeline-trigger',
-    '--my-trigger view() once contain 20% contain 80% cover 10%',
-    '--my-trigger view() once contain 20% contain 80% cover 10% cover');
+    '--my-trigger view() contain 20% contain 80% cover 10%',
+    '--my-trigger view() contain 20% contain 80% cover 10% cover');
   test_computed_value('timeline-trigger',
-      '--my-trigger view() once contain 0% contain 100%',
-      '--my-trigger view() once contain contain auto auto');
+      '--my-trigger view() contain 0% contain 100%',
+      '--my-trigger view() contain contain auto auto');
   test_computed_value('timeline-trigger',
-      'view() once',
-      'none view() once normal normal auto auto');
+      'view()',
+      'none view() normal normal auto auto');
   test_computed_value('timeline-trigger',
       '--my-trigger --my-timeline',
-      '--my-trigger --my-timeline once normal normal auto auto');
+      '--my-trigger --my-timeline normal normal auto auto');
   test_computed_value('timeline-trigger',
-      'once contain 0% contain 100% --my-trigger --my-timeline',
-      '--my-trigger --my-timeline once contain contain auto auto');
+      'contain 0% contain 100% --my-trigger --my-timeline',
+      '--my-trigger --my-timeline contain contain auto auto');
 
   test_computed_value('timeline-trigger',
       '--trigger1, --trigger2',
-      '--trigger1 auto once normal normal auto auto, ' +
-      '--trigger2 auto once normal normal auto auto');
-
-  test_computed_value('timeline-trigger',
-      '--trigger1 repeat, --trigger2 alternate',
-      '--trigger1 auto repeat normal normal auto auto, ' +
-      '--trigger2 auto alternate normal normal auto auto');
+      '--trigger1 auto normal normal auto auto, ' +
+      '--trigger2 auto normal normal auto auto');
 
   test_computed_value('timeline-trigger',
       '--trigger1 view(), --trigger2 scroll()',
-      '--trigger1 view() once normal normal auto auto, ' +
-      '--trigger2 scroll() once normal normal auto auto');
+      '--trigger1 view() normal normal auto auto, ' +
+      '--trigger2 scroll() normal normal auto auto');
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/url/IdnaTestV2-removed.window-expected.txt b/third_party/blink/web_tests/external/wpt/url/IdnaTestV2-removed.window-expected.txt
deleted file mode 100644
index efa5660..0000000
--- a/third_party/blink/web_tests/external/wpt/url/IdnaTestV2-removed.window-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] ToASCII("ᡯ⚉姶🄉.۷‍🎪‍") C2; P1; V6
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/url/IdnaTestV2.window-expected.txt b/third_party/blink/web_tests/external/wpt/url/IdnaTestV2.window-expected.txt
index 8a1dcdd..f42b5ab 100644
--- a/third_party/blink/web_tests/external/wpt/url/IdnaTestV2.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/url/IdnaTestV2.window-expected.txt
@@ -1,19 +1,7 @@
 This is a testharness.js-based test.
-Found 978 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] ToASCII("a‌b") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("A‌B") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("A‌b") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
+Found 806 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] ToASCII("xn--ab-j1t") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("a‍b") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("A‍B") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("A‍b") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--ab-m1t") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--u-ccb") V1
@@ -28,62 +16,26 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--xn--a--gua.pt") V4; V2 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("1.aß‌‍b‌‍cßßßßdςσßßßßßßßßeßßßßßßßßßßxßßßßßßßßßßyßßßßßßßß̂ßz") C1; C2; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("1.ASS‌‍B‌‍CSSSSSSSSDΣΣSSSSSSSSSSSSSSSSESSSSSSSSSSSSSSSSSSSSXSSSSSSSSSSSSSSSSSSSSYSSSSSSSSSSSSSSSŜSSZ") C1; C2; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("1.ASS‌‍B‌‍CSSSSSSSSDΣΣSSSSSSSSSSSSSSSSESSSSSSSSSSSSSSSSSSSSXSSSSSSSSSSSSSSSSSSSSYSSSSSSSSSSSSSSSŜSSZ") C1; C2; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("1.ass‌‍b‌‍cssssssssdσσssssssssssssssssessssssssssssssssssssxssssssssssssssssssssysssssssssssssssŝssz") C1; C2; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("1.ass‌‍b‌‍cssssssssdσσssssssssssssssssessssssssssssssssssssxssssssssssssssssssssysssssssssssssssŝssz") C1; C2; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("1.Ass‌‍b‌‍cssssssssdσσssssssssssssssssessssssssssssssssssssxssssssssssssssssssssysssssssssssssssŝssz") C1; C2; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("1.Ass‌‍b‌‍cssssssssdσσssssssssssssssssessssssssssssssssssssxssssssssssssssssssssysssssssssssssssŝssz") C1; C2; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("1.xn--assbcssssssssdssssssssssssssssessssssssssssssssssssxssssssssssssssssssssysssssssssssssssssz-pxq1419aa69989dba9gc") C1; C2; A4_2 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("1.Aß‌‍b‌‍cßßßßdςσßßßßßßßßeßßßßßßßßßßxßßßßßßßßßßyßßßßßßßß̂ßz") C1; C2; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("1.xn--abcdexyz-qyacaaabaaaaaaabaaaaaaaaabaaaaaaaaabaaaaaaaa010ze2isb1140zba8cc") C1; C2; A4_2 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌x‍n‌-‍-bß") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌X‍N‌-‍-BSS") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌x‍n‌-‍-bss") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌X‍n‌-‍-Bss") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--xn--bss-7z6ccid") C1; C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌X‍n‌-‍-Bß") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--xn--b-pqa5796ccahd") C1; C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--xn---epa") V4; V2 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("a.b.xn--c-bcb.d") V6
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ஹ‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--dmc225h") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1ug") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ஹ‌") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--dmc025h") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ug") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ۯ‌ۯ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--cmba004q") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--") P4; A4_1 (ignored); A4_2 (ignored)
@@ -98,10 +50,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--7hb713lfwbi1311b.-") V7; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍≠ᢙ≯.솣-ᡴႠ") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍≠ᢙ≯.솣-ᡴⴀ") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("≠ᢙ≯.솣-ᡴⴀ")
   Failed to construct 'URL': Invalid URL
 [FAIL] ToASCII("≠ᢙ≯.솣-ᡴႠ")
@@ -130,14 +78,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII(".xn--ph4h") V6; A4_2 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ß۫。‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("SS۫。‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ss۫。‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Ss۫。‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--ss-59d.xn--1ug") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--zca012a.xn--1ug") C2
@@ -230,10 +170,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--8-xmb44974n.xn--su6h") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌긃.榶-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌긃.榶-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ug3307c.xn----d87b") C1; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--lwwp69lqs7m.xn--b7b") V6
@@ -258,24 +194,8 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--hnd212gz32d54x5r.xn--y86c") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Å둄-.‌") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Å둄-.‌") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Å둄-.‌") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Å둄-.‌") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("å둄-.‌") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("å둄-.‌") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----1fa1788k.xn--0ug") C1; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("å둄-.‌") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("å둄-.‌") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--ct2b0738h.xn--772h.") V6; A4_2 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ugb3358ili2v.xn--772h.") C1; C2; V6; A4_2 (ignored)
@@ -418,10 +338,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--hdh5636g.xn--ci2d") V6
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Ⴋ≮𱲆。‍ާ𐋣") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ⴋ≮𱲆。‍ާ𐋣") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--gdhz03bxt42d.xn--lrb6479j") V6
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--gdhz03bxt42d.xn--lrb506jqr4n") C2
@@ -446,18 +362,12 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--210d.-") V6; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ꡦᡑ‍1.。𐋣-") C2; V3 (ignored); A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1-o7j663bdl7m..xn----381i") C2; V3 (ignored); A4_2 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--h8e863drj7h.xn----381i") V7; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--h8e470bl0d838o.xn----381i") C2; V7; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("1.䰹‍-。웈") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("1.䰹‍-。웈") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("1.xn----tgnz80r.xn--kp5b") C2; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----dcp160o.xn--kp5b") V7; V3 (ignored)
@@ -528,8 +438,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--e1g71d.xn--772h") V6
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌.≯") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ug.xn--hdh") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----7m53aj640l.xn----8f4br83t") V6; V7; V3 (ignored)
@@ -572,14 +480,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--hdh84f.xn--zca") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌。≠") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌。≠") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌。≠") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌。≠") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ug.xn--1ch") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--461dw464a.xn--v8e29loy65a") V6
@@ -650,10 +550,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--u836e.xn----qfa750ve7b") C1; V7; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ᡙ‌。≯𐋲≠") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ᡙ‌。≯𐋲≠") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("ᡙ.≯𐋲≠")
   Failed to construct 'URL': Invalid URL
 [FAIL] ToASCII("xn--p8e650b.xn--1ch3a7084l") C1
@@ -676,28 +572,10 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--9zh3057f.xn--j7e103b") V6
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("-3.‍ヌᢕ") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("-3.xn--fbf739aq5o") C2; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Ⅎ្‍。≠‍‌") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Ⅎ្‍。≠‍‌") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Ⅎ្‍。≠‍‌") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Ⅎ្‍。≠‍‌") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ⅎ្‍。≠‍‌") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ⅎ្‍。≠‍‌") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--u4e823bq1a.xn--0ugb89o") C1; C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ⅎ្‍。≠‍‌") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ⅎ្‍。≠‍‌") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--u4e319b.xn--1ch") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--u4e823bcza.xn--0ugb89o") C1; C2; V7
@@ -708,22 +586,10 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--l76a726rt2h.xn--3xa") V6
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ς-。‌𝟭-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ς-。‌1-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Σ-。‌1-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("σ-。‌1-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----zmb.xn--1--i1t") C1; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----xmb.xn--1--i1t") C1; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Σ-。‌𝟭-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("σ-。‌𝟭-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----ggf830f.xn--vkj") V6
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----ggf830f.xn--cnd") V6; V7
@@ -750,10 +616,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1ug30527h9mxi.xn--k0o") C2; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ᡯ⚉姶🄉.۷‍🎪‍") C2; U1 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ᡯ⚉姶8,.۷‍🎪‍") C2; U1 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--8,-g9oy26fzu4d.xn--kmb859ja94998b") C2; U1 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--c9e433epi4b3j20a.xn--kmb6733w") V7
@@ -772,10 +634,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--792h.xn----bse632b") V6; V7; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("𝟵隁⯮.᠍‌") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("9隁⯮.᠍‌") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--9-mfs8024b.xn--0ug") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--mta176jjjm.c") V6
@@ -878,30 +736,10 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--mbm8237g.xn--7-7hf1526p") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ß‌꫶ᢥ.⊶ჁႶ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ß‌꫶ᢥ.⊶ჁႶ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ß‌꫶ᢥ.⊶ⴡⴖ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("SS‌꫶ᢥ.⊶ჁႶ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ss‌꫶ᢥ.⊶ⴡⴖ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Ss‌꫶ᢥ.⊶Ⴡⴖ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--ss-4ep585bkm5p.xn--ifh802b6a") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--zca682johfi89m.xn--ifh802b6a") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ß‌꫶ᢥ.⊶ⴡⴖ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("SS‌꫶ᢥ.⊶ჁႶ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ss‌꫶ᢥ.⊶ⴡⴖ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Ss‌꫶ᢥ.⊶Ⴡⴖ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--ss-4epx629f.xn--5nd703gyrh") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--ss-4ep585bkm5p.xn--5nd703gyrh") C1; V7
@@ -922,22 +760,10 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--3shy698frsu9dt1me.xn----x310m") V7; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("-。‍") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("-。‍") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("-.xn--1ug") C2; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--d0d41273c887z.xn--8-ob5i") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ς‍-.Ⴣ𦟙") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ς‍-.ⴣ𦟙") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Σ‍-.Ⴣ𦟙") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("σ‍-.ⴣ𦟙") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----zmb048s.xn--rlj2573p") C2; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----xmb348s.xn--rlj2573p") C2; V3 (ignored)
@@ -974,10 +800,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1ug1658ftw26f.xn----t2c071q") C1; C2; V7; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍.j") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍.J") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1ug.j") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--5cb172r175fug38a.xn--mlj") V7
@@ -1002,8 +824,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--cld333gn31h0158l.xn--3g0d") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("鱊。‌") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--rt6a.xn--0ug") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--dj8y.") V7; A4_2 (ignored)
@@ -1028,26 +848,10 @@
   Failed to construct 'URL': Invalid URL
 [FAIL] ToASCII("⾆.ꡈ5≯Ss")
   Failed to construct 'URL': Invalid URL
-[FAIL] ToASCII("‌Ⴚ。ς") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌Ⴚ。ς") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌ⴚ。ς") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌Ⴚ。Σ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌ⴚ。σ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ug262c.xn--4xa") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ug262c.xn--3xa") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌ⴚ。ς") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌Ⴚ。Σ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌ⴚ。σ") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--ynd.xn--4xa") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--ynd.xn--3xa") V7
@@ -1056,12 +860,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--ynd759e.xn--3xa") C1; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍⾕。‌꥓̐ꡎ") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍⾕。‌꥓̐ꡎ") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍谷。‌꥓̐ꡎ") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--6g3a.xn--0sa8175flwa") V6
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1ug0273b.xn--0sa359l6n7g13a") C1; C2
@@ -1080,8 +878,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--zca20040bgrkh.xn--zca3653v86qa") V6; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍。‌") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1ug.xn--0ug") C1; C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--s136e.") V7; A4_2 (ignored)
@@ -1090,42 +886,22 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--ym9av13acp85w.xn--dth22121k") V6; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌。。") C1; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ug..") C1; A4_2 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII(".xn--y86c") V7; A4_2 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ug.xn--y86c") C1; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ᡲ-𝟹.ß-‌-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ᡲ-3.ß-‌-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ᡲ-3.SS-‌-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ᡲ-3.ss-‌-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ᡲ-3.Ss-‌-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn---3-p9o.xn--ss---276a") C1; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn---3-p9o.xn-----fia9303a") C1; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ᡲ-𝟹.SS-‌-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ᡲ-𝟹.ss-‌-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ᡲ-𝟹.Ss-‌-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--ibf35138o.xn--fpfz94g") V6; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--3-rj42h.1.xn--2-13k96240l") V6; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--3-rj42h.xn--2-13k746cq465x") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍51.。≯8‍") C2; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--51-l1t..xn--8-ugn00i") C2; A4_2 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--5-ecp.xn--8-ogo") V7
@@ -1146,10 +922,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--b5q.xn--v7e6041kqqd4m251b") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("𝟙。‍𝟸‍⁷") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("1。‍2‍7") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("1.xn--27-l1tb") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----z8j.xn--1-5671m") V7; V3 (ignored)
@@ -1180,20 +952,12 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----scp6252h.xn--zshy411yzpx2d") V7; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌Ⴁ畝‍.≮") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌Ⴁ畝‍.≮") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌ⴁ畝‍.≮") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("ⴁ畝.≮")
   Failed to construct 'URL': Invalid URL
 [FAIL] ToASCII("Ⴁ畝.≮")
   Failed to construct 'URL': Invalid URL
 [FAIL] ToASCII("xn--0ugc160hb36e.xn--gdh") C1; C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌ⴁ畝‍.≮") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--8md0962c.xn--gdh") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--8md700fea3748f.xn--gdh") C1; C2; V7
@@ -1304,16 +1068,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--f3g73398c.xn--hdhz171b") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌.ßႩ-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌.ßⴉ-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌.SSႩ-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌.ssⴉ-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌.Ssⴉ-") C1; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ug.xn--ss--bi1b") C1; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ug.xn----pfa2305a") C1; V3 (ignored)
@@ -1354,16 +1108,8 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----ogot9g.xn----n89hl0522az9u2a") V7; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Ⴏ󠅋-.‍Ⴉ") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("Ⴏ󠅋-.‍Ⴉ") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ⴏ󠅋-.‍ⴉ") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----3vs.xn--1ug532c") C2; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("ⴏ󠅋-.‍ⴉ") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----00g.xn--hnd") V7; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----00g.xn--hnd399e") C2; V7; V3 (ignored)
@@ -1390,10 +1136,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("9.xn--dbf91222q") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("。Ⴖͦ.‌") C1; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("。ⴖͦ.‌") C1; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII(".xn--hva754s.xn--0ug") C1; A4_2 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--hva754sy94k.") V7; A4_2 (ignored)
@@ -1438,30 +1180,18 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--qutw175s.xn----mimu6tf67j") V7; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍攌꯭。ᢖ-Ⴘ") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍攌꯭。ᢖ-ⴘ") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1ug592ykp6b.xn----mck373i") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--p9ut19m.xn----k1g451d") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1ug592ykp6b.xn----k1g451d") C2; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌ꖨ.16.3툒۳") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌ꖨ.16.3툒۳") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ug2473c.16.xn--3-nyc0117m") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--9r8a.xn--3-nyc678tu07m") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ug2473c.xn--3-nyc678tu07m") C1; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("𝟏𝨙⸖.‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("1𝨙⸖.‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1-5bt6845n.xn--1ug") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("f.xn--45hz6953f") V7
@@ -1480,8 +1210,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--27e.xn--7cy81125a0yq4a") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌‌。1.≯9") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0uga.1.xn--9-ogo") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII(".xn--9-ogo37g") V7; A4_2 (ignored)
@@ -1510,10 +1238,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--zca679eh2l.xn--tsh") C2; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("𝟠≮‌。󠅱឴") C1; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("8≮‌。󠅱឴") C1; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("8≮.") A4_2 (ignored)
   Failed to construct 'URL': Invalid URL
 [FAIL] ToASCII("xn--8-sgn10i.") C1; A4_2 (ignored)
@@ -1580,10 +1304,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--lnd.xn--0ug56448b") C1; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("-‍.Ⴞ𐋷") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("-‍.ⴞ𐋷") C2; V3 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----ugn.xn--mlj8559d") C2; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("-.xn--2nd2315j") V7; V3 (ignored)
@@ -1598,14 +1318,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--zca19ln1di19a.xn--xmc") C2; V6
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("≠.‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("≠.‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("≠.‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("≠.‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1ch.xn--1ug") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--4xa502av8297a.xn--4xa6055k") V7
@@ -1620,10 +1332,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--j890g.xn--w7e") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("嬃𝍌.‍ୄ") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("嬃𝍌.‍ୄ") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--b6s0078f.xn--0ic") V6
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--b6s0078f.xn--0ic557h") C2
@@ -1638,32 +1346,12 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("-.xn--y86c") V7; V3 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍.F") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍.f") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1ug.f") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍㨲。ß") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍㨲。ß") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍㨲。SS") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍㨲。ss") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍㨲。Ss") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1ug914h.ss") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1ug914h.xn--zca") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍㨲。SS") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍㨲。ss") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍㨲。Ss") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII(".xn--h327f") V7; A4_2 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1ug.xn--h327f") C2; V7
@@ -1686,14 +1374,8 @@
   Failed to construct 'URL': Invalid URL
 [FAIL] ToASCII("xn--jnd1986v.xn--gdh") V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("璼𝨭。‌󠇟") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("璼𝨭。‌󠇟") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--gky8837e.xn--0ug") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‌.‌") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ug.xn--0ug") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--flj.xn--qdb0605f14ycrms3c") V6; V7
@@ -1718,10 +1400,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--l98a.xn--dgd218hhp28d") V6; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("𝟠4󠇗𝈻.‍𐋵⛧‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("84󠇗𝈻.‍𐋵⛧‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--84-s850a.xn--1uga573cfq1w") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("≮𝟕.謖ß≯")
@@ -1748,28 +1426,10 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--tsh94183d.xn--2-rgn") C1; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍‌󠆪。ß𑓃") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍‌󠆪。ß𑓃") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍‌󠆪。SS𑓃") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍‌󠆪。ss𑓃") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍‌󠆪。Ss𑓃") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ugb.xn--ss-bh7o") C1; C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--0ugb.xn--zca0732l") C1; C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍‌󠆪。SS𑓃") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍‌󠆪。ss𑓃") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("‍‌󠆪。Ss𑓃") C1; C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("。‌ヶ䒩.ꡪ") C1; A4_2 (ignored)
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII(".xn--0ug287dj0o.xn--gd9a") C1; A4_2 (ignored)
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--qekw60dns9k.xn--gd9a") V7
@@ -1906,8 +1566,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--7pj.xn--zca870n") C2; V7
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("梉。‌") C1
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--7zv.xn--0ug") C1
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn----n50a258u.xn---1-up07j.") V7; V3 (ignored); A4_2 (ignored)
@@ -1942,8 +1600,6 @@
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--1chy468a.xn--2ed") V6
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
-[FAIL] ToASCII("𐋷。‍") C2
-  assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--r97c.xn--1ug") C2
   assert_throws_js: function "() => new URL(`https://${idnaTest.input}/x`)" did not throw
 [FAIL] ToASCII("xn--2g1d14o.xn--jti") V6
diff --git a/third_party/blink/web_tests/external/wpt/url/toascii.window-expected.txt b/third_party/blink/web_tests/external/wpt/url/toascii.window-expected.txt
index c4d6720..484a152 100644
--- a/third_party/blink/web_tests/external/wpt/url/toascii.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/url/toascii.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 117 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 108 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] xn--a (using URL)
   assert_throws_js: function "() => makeURL("url", hostTest.input)" did not throw
 [FAIL] xn--a (using URL.host)
@@ -54,24 +54,6 @@
   assert_equals: expected "x" but got "xn--ls8h="
 [FAIL] xn--ls8h= (using <area>.hostname)
   assert_equals: expected "x" but got "xn--ls8h="
-[FAIL] ‍.example (using URL)
-  assert_throws_js: function "() => makeURL("url", hostTest.input)" did not throw
-[FAIL] ‍.example (using URL.host)
-  assert_equals: expected "x" but got "xn--1ug.example"
-[FAIL] ‍.example (using URL.hostname)
-  assert_equals: expected "x" but got "xn--1ug.example"
-[FAIL] ‍.example (using <a>)
-  assert_equals: expected "" but got "xn--1ug.example"
-[FAIL] ‍.example (using <a>.host)
-  assert_equals: expected "x" but got "xn--1ug.example"
-[FAIL] ‍.example (using <a>.hostname)
-  assert_equals: expected "x" but got "xn--1ug.example"
-[FAIL] ‍.example (using <area>)
-  assert_equals: expected "" but got "xn--1ug.example"
-[FAIL] ‍.example (using <area>.host)
-  assert_equals: expected "x" but got "xn--1ug.example"
-[FAIL] ‍.example (using <area>.hostname)
-  assert_equals: expected "x" but got "xn--1ug.example"
 [FAIL] xn--1ug.example (using URL)
   assert_throws_js: function "() => makeURL("url", hostTest.input)" did not throw
 [FAIL] xn--1ug.example (using URL.host)
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-alternate.tentative.html b/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-alternate.tentative.html
index 5be5738e..b0bc7a5 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-alternate.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-alternate.tentative.html
@@ -27,7 +27,6 @@
             { duration: ANIMATION_DURATION_MS, fill: "both" }
           ));
         const trigger = new EventTrigger({
-          behavior: "alternate",
           eventType: "click",
           eventTarget: eventTarget
         });
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-before-handlers.tentative.html b/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-before-handlers.tentative.html
index d2e9ebd..0ed167f 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-before-handlers.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-before-handlers.tentative.html
@@ -34,7 +34,6 @@
             "Animation is already running when event listener runs");
         });
         const trigger = new EventTrigger({
-          behavior: "once",
           eventType: "click",
           eventTarget: eventTarget
         });
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-once.tentative.html b/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-once.tentative.html
index f19d0fa..2d02adb 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-once.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-once.tentative.html
@@ -27,7 +27,6 @@
             { duration: ANIMATION_DURATION_MS, fill: "both" }
           ));
         const trigger = new EventTrigger({
-          behavior: "once",
           eventType: "click",
           eventTarget: eventTarget
         });
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-repeat.tentative.html b/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-repeat.tentative.html
index 0dace937..6cf232a 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-repeat.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-repeat.tentative.html
@@ -27,7 +27,6 @@
             { duration: ANIMATION_DURATION_MS, fill: "both" }
           ));
         const trigger = new EventTrigger({
-          behavior: "repeat",
           eventType: "click",
           eventTarget: eventTarget
         });
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-state.tentative.html b/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-state.tentative.html
index 70a71f9..7e48f3e 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-state.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-trigger/event-trigger-state.tentative.html
@@ -27,7 +27,6 @@
             { duration: ANIMATION_DURATION_MS, fill: "both" }
           ));
         const trigger = new EventTrigger({
-          behavior: "state",
           eventType: "click",
           eventTarget: eventTarget
         });
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
index 4e1ca35..9fd8df7 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
@@ -409,7 +409,6 @@
 text-wrap-mode: wrap
 text-wrap-style: auto
 timeline-scope: none
-timeline-trigger-behavior: once
 timeline-trigger-exit-range-end: auto
 timeline-trigger-exit-range-start: auto
 timeline-trigger-name: none
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
index 773ac06..418135b 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
@@ -409,7 +409,6 @@
 text-wrap-mode: wrap
 text-wrap-style: auto
 timeline-scope: none
-timeline-trigger-behavior: once
 timeline-trigger-exit-range-end: auto
 timeline-trigger-exit-range-start: auto
 timeline-trigger-name: none
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/browser-set-permission-iframe.js b/third_party/blink/web_tests/http/tests/inspector-protocol/browser-set-permission-iframe.js
index 2f769fa..5ea72f0 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/browser-set-permission-iframe.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/browser-set-permission-iframe.js
@@ -19,7 +19,7 @@
   // Grant and query the storage access permission.
   await queryPermission(iframeSession, storage_access_descriptor, 'prompt');
   await setPermission(
-      storage_access_descriptor, 'granted', requestingUrl, embeddingUrl);
+      storage_access_descriptor, 'granted', embeddingUrl, requestingUrl);
   await queryPermission(iframeSession, storage_access_descriptor, 'granted');
 
   // Navigate to a new embedding url.
@@ -44,17 +44,17 @@
   // message.
   await setPermission(storage_access_descriptor, 'granted', 'data:text/html,');
   await setPermission(
-      storage_access_descriptor, 'granted', requestingUrl, 'data:text/html,');
+      storage_access_descriptor, 'granted', 'data:text/html,', requestingUrl);
 
   testRunner.completeTest();
 
-  async function setPermission(permission, setting, origin, embeddingOrigin) {
+  async function setPermission(permission, setting, origin, embeddedOrigin) {
     const params = {permission, setting};
     if (origin) {
       params.origin = origin;
     }
-    if (embeddingOrigin) {
-      params.embeddingOrigin = embeddingOrigin;
+    if (embeddedOrigin) {
+      params.embeddedOrigin = embeddedOrigin;
     }
     const response = await dp.Browser.setPermission(params);
     if (response.error) {
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/url-blocking-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/url-blocking-expected.txt
index 0669ea4..b7b769c 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/url-blocking-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/url-blocking-expected.txt
@@ -25,4 +25,12 @@
     id : <number>
     sessionId : <string>
 }
+{
+    error : {
+        code : -32602
+        message : Pattern "*.css" failed to parse as a URLPattern.
+    }
+    id : <number>
+    sessionId : <string>
+}
 
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/url-blocking.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/url-blocking.js
index 4309b97..01a89b51 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/url-blocking.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/url-blocking.js
@@ -59,8 +59,8 @@
   await testRequest('resources/b.html', false);
 
   testRunner.log('\nTest setting patterns that fail to parse:');
-  const result = await dp.Network.setBlockedURLs({urlPatterns: ['*://*', 'ht tp://']});
-  testRunner.log(result);
+  testRunner.log(await dp.Network.setBlockedURLs({urlPatterns: ['ht tp://']}));
+  testRunner.log(await dp.Network.setBlockedURLs({urlPatterns: ['*.css']}));
 
   testRunner.completeTest();
 })
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/preload/preloading-attempt-sources-updated-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/preload/preloading-attempt-sources-updated-expected.txt
index e6b41cba..2cdecd6 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/preload/preloading-attempt-sources-updated-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/preload/preloading-attempt-sources-updated-expected.txt
@@ -80,6 +80,18 @@
             [0] : <rule-set-2>
         ]
     }
+    [4] : {
+        key : {
+            action : PrerenderUntilScript
+            loaderId : <string>
+            url : http://127.0.0.1:8000/pus.html
+        }
+        nodeIds : [
+        ]
+        ruleSetIds : [
+            [0] : <rule-set-2>
+        ]
+    }
 ]
 
 Running test: documentRules
@@ -164,6 +176,19 @@
             [0] : <rule-set-2>
         ]
     }
+    [6] : {
+        key : {
+            action : PrerenderUntilScript
+            loaderId : <string>
+            url : http://127.0.0.1:8000/pus.html
+        }
+        nodeIds : [
+            [0] : <link-8>
+        ]
+        ruleSetIds : [
+            [0] : <rule-set-2>
+        ]
+    }
 ]
 
 Running test: duplicateRuleSets
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/preload/preloading-attempt-sources-updated.js b/third_party/blink/web_tests/http/tests/inspector-protocol/preload/preloading-attempt-sources-updated.js
index 3b3207e..e55acb97 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/preload/preloading-attempt-sources-updated.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/preload/preloading-attempt-sources-updated.js
@@ -115,6 +115,10 @@
               "prerender": [{
                 "source": "list",
                 "urls": ["/two.html"]
+              }],
+              "prerender_until_script": [{
+                "source": "list",
+                "urls": ["/pus.html"]
               }]
             }
           </script>
@@ -157,6 +161,10 @@
               "prerender": [{
                 "source": "document",
                 "where": {"selector_matches": ".important-links a"}
+              }],
+              "prerender_until_script": [{
+                "source": "document",
+                "where": {"selector_matches": ".pus-links a"}
               }]
             }
           </script>
@@ -175,6 +183,9 @@
               <a href="/sol.html"></a>
               <a href="/time.html"></a>
             </div>
+            <div class="pus-links">
+              <a href="/pus.html"></a>
+            </div>
         </body>
       </html>
     `);
@@ -193,13 +204,16 @@
     const miscLinks = (await dp.DOM.querySelectorAll({
       nodeId: documentNodeId, selector: ".misc-links a"
     })).result.nodeIds;
+    const pusLinks = (await dp.DOM.querySelectorAll({
+      nodeId: documentNodeId, selector: ".pus-links a"
+    })).result.nodeIds;
 
     testRunner.log(
       await formatPreloadingAttemptSources(
         dp,
         preloadAttemptSources,
         [ruleSet1.id, ruleSet2.id],
-        [...importantLinks, ...lessImportantLinks, ...miscLinks]),
+        [...importantLinks, ...lessImportantLinks, ...miscLinks, ...pusLinks]),
       "Preload attempts: ");
   }
 
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/preload/rule-set-updated-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/preload/rule-set-updated-expected.txt
index 54c822f..066ae616 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/preload/rule-set-updated-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/preload/rule-set-updated-expected.txt
@@ -38,6 +38,24 @@
     tag : prerender_tag
 }
 {
+    _selector : #prerender-until-script
+    backendNodeId : <number>
+    id : <string>
+    loaderId : <string>
+    sourceText : {
+        prerender_until_script : [
+            [0] : {
+                source : list
+                urls : [
+                    [0] : /page.html?pus=1
+                ]
+            }
+        ]
+        tag : prerender_until_script_tag
+    }
+    tag : prerender_until_script_tag
+}
+{
     _selector : #invalid-json
     backendNodeId : <number>
     errorMessage : Line: 4, column: 7, Syntax error.
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/preload/rule-set-updated.js b/third_party/blink/web_tests/http/tests/inspector-protocol/preload/rule-set-updated.js
index 2175d73a..98c4773 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/preload/rule-set-updated.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/preload/rule-set-updated.js
@@ -25,6 +25,17 @@
           ]
         }
       </script>
+      <script type="speculationrules" id="prerender-until-script">
+        {
+          "tag": "prerender_until_script_tag",
+          "prerender_until_script":[
+            {
+              "source": "list",
+              "urls": ["/page.html?pus=1"]
+            }
+          ]
+        }
+      </script>
       <script type="speculationrules" id="invalid-json">
         {
           "prefetch":[
@@ -71,6 +82,7 @@
     const selectors = [
       '#prefetch',
       '#prerender',
+      '#prerender-until-script',
       '#invalid-json',
       '#not-object',
       '#contains-invalid-rule',
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/prerender/prerender-until-script-status-update-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/prerender/prerender-until-script-status-update-expected.txt
new file mode 100644
index 0000000..985f8eb
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/prerender/prerender-until-script-status-update-expected.txt
@@ -0,0 +1,28 @@
+Tests that Preload.prerenderStatusUpdated is sent for speculation rule with "prerender_until_script" action.
+{
+    method : Preload.prerenderStatusUpdated
+    params : {
+        key : {
+            action : PrerenderUntilScript
+            loaderId : <string>
+            url : http://127.0.0.1:8000/inspector-protocol/prerender/resources/prerender.html
+        }
+        pipelineId : <string>
+        status : Pending
+    }
+    sessionId : <string>
+}
+{
+    method : Preload.prerenderStatusUpdated
+    params : {
+        key : {
+            action : PrerenderUntilScript
+            loaderId : <string>
+            url : http://127.0.0.1:8000/inspector-protocol/prerender/resources/prerender.html
+        }
+        pipelineId : <string>
+        status : Running
+    }
+    sessionId : <string>
+}
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/prerender/prerender-until-script-status-update.js b/third_party/blink/web_tests/http/tests/inspector-protocol/prerender/prerender-until-script-status-update.js
new file mode 100644
index 0000000..95582eb
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/prerender/prerender-until-script-status-update.js
@@ -0,0 +1,20 @@
+(async function(testRunner) {
+  const {tabTargetSession} = await testRunner.startBlankWithTabTarget(
+      'Tests that Preload.prerenderStatusUpdated is sent for speculation rule with "prerender_until_script" action.');
+
+  const childTargetManager =
+      new TestRunner.ChildTargetManager(testRunner, tabTargetSession);
+  await childTargetManager.startAutoAttach();
+  const session1 = childTargetManager.findAttachedSessionPrimaryMainFrame();
+  const dp1 = session1.protocol;
+  await dp1.Preload.enable();
+
+  session1.navigate(testRunner.url('resources/prerender-until-script.html'));
+
+  // Pending
+  testRunner.log(await dp1.Preload.oncePrerenderStatusUpdated());
+  // Running
+  testRunner.log(await dp1.Preload.oncePrerenderStatusUpdated());
+
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/prerender/resources/prerender-until-script.html b/third_party/blink/web_tests/http/tests/inspector-protocol/prerender/resources/prerender-until-script.html
new file mode 100644
index 0000000..6db1ece
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/prerender/resources/prerender-until-script.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script type="speculationrules">
+{
+  "prerender_until_script": [{
+    "source": "list",
+    "urls": ["prerender.html"]
+  }]
+}
+</script>
diff --git a/third_party/blink/web_tests/http/tests/messaging/message-event-slow-deserialization-expected.txt b/third_party/blink/web_tests/http/tests/messaging/message-event-slow-deserialization-expected.txt
index 7f079c8c..06e6ed9ee 100644
--- a/third_party/blink/web_tests/http/tests/messaging/message-event-slow-deserialization-expected.txt
+++ b/third_party/blink/web_tests/http/tests/messaging/message-event-slow-deserialization-expected.txt
@@ -1,4 +1,3 @@
 Used slow mode for cross-origin message with data accessed after origin: false
-Used slow mode for cross-origin message with data accessed before origin (long message): true
-Used slow mode for cross-origin message with data accessed before origin (short message): false
+Used slow mode for cross-origin message with data accessed before origin: true
 Used slow mode for same-origin message with data accessed before origin: false
diff --git a/third_party/blink/web_tests/http/tests/messaging/message-event-slow-deserialization.html b/third_party/blink/web_tests/http/tests/messaging/message-event-slow-deserialization.html
index e5bee21..361799f 100644
--- a/third_party/blink/web_tests/http/tests/messaging/message-event-slow-deserialization.html
+++ b/third_party/blink/web_tests/http/tests/messaging/message-event-slow-deserialization.html
@@ -8,15 +8,13 @@
   testRunner.waitUntilDone();
 }
 
-const doPost = function (payloadSize) {
-  top.postMessage({payload: "don't panic" + Array(payloadSize).join('!')}, '*');
+const doPost = function () {
+  top.postMessage({payload: "don't panic"}, '*');
 }
 
-function createCrossOriginIframeAndPostMessage(short) {
-  const payloadSize = short ? 0 : 16 * 1024;
+function createCrossOriginIframeAndPostMessage() {
   const iframe = document.createElement("iframe");
-  iframe.src = "data:text/html,<" +
-      `script type="application/x-javascript">(${doPost.toString()})(${payloadSize})</` + "script>";
+  iframe.src = "data:text/html,<" + `script type="application/x-javascript">(${doPost.toString()})()</` + "script>";
   document.body.appendChild(iframe);
 }
 
@@ -34,7 +32,7 @@
 
 (async function() {
   internals.clearUseCounter(document, WebFeature.kSlowDeserialization);
-  createCrossOriginIframeAndPostMessage(/* short */ false);
+  createCrossOriginIframeAndPostMessage();
   const crossOriginEvent1 = await new Promise(resolve => {
     window.addEventListener('message', resolve, {once: true});
   });
@@ -44,23 +42,13 @@
       internals.isUseCounted(document, WebFeature.kSlowDeserialization));
 
   internals.clearUseCounter(document, WebFeature.kSlowDeserialization);
-  createCrossOriginIframeAndPostMessage(/* short */ false);
+  createCrossOriginIframeAndPostMessage();
   const crossOriginEvent2 = await new Promise(resolve => {
     window.addEventListener('message', resolve, {once: true});
   });
   crossOriginEvent2.data;
   crossOriginEvent2.origin;
-  output('Used slow mode for cross-origin message with data accessed before origin (long message): ' +
-      internals.isUseCounted(document, WebFeature.kSlowDeserialization));
-
-  internals.clearUseCounter(document, WebFeature.kSlowDeserialization);
-  createCrossOriginIframeAndPostMessage(/* short */ true);
-  const crossOriginEvent3 = await new Promise(resolve => {
-    window.addEventListener('message', resolve, {once: true});
-  });
-  crossOriginEvent3.data;
-  crossOriginEvent3.origin;
-  output('Used slow mode for cross-origin message with data accessed before origin (short message): ' +
+  output('Used slow mode for cross-origin message with data accessed before origin: ' +
       internals.isUseCounted(document, WebFeature.kSlowDeserialization));
 
   internals.clearUseCounter(document, WebFeature.kSlowDeserialization);
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 5ee7aaa4..d271520 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -1481,6 +1481,7 @@
     setter textRendering
     setter wordSpacing
 interface Origin
+    static method from
     static method fromURL
     static method parse
     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/http/tests/test-runner/resources/storage-isolation-test-util.js b/third_party/blink/web_tests/http/tests/test-runner/resources/storage-isolation-test-util.js
new file mode 100644
index 0000000..8445205
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/test-runner/resources/storage-isolation-test-util.js
@@ -0,0 +1,63 @@
+(function() {
+async function createAllData(identifier) {
+  localStorage.setItem(`key_${identifier}`, 'data');
+  await new Promise(r => indexedDB.open(`db_${identifier}`, 1).onsuccess = r);
+  await caches.open(`cache_${identifier}`);
+
+  const opfsRoot = await navigator.storage.getDirectory();
+  const fileHandle =
+      await opfsRoot.getFileHandle(`file_${identifier}.txt`, {create: true});
+  const writable = await fileHandle.createWritable();
+  await writable.write('data');
+  await writable.close();
+}
+
+async function verifyStorageIsEmptyAndLogDetails() {
+  let isOverallClean = true;
+  function check(isClean, cleanMessage, dirtyMessage) {
+    if (isClean) {
+      console.log(`  OK: ${cleanMessage}`);
+    } else {
+      console.log(`  FAIL: ${dirtyMessage}`);
+      isOverallClean = false;
+    }
+  }
+
+  check(
+      localStorage.length === 0, 'LocalStorage is empty.',
+      `LocalStorage is not empty. Found ${localStorage.length} items.`);
+  const idbDatabases = await indexedDB.databases();
+  check(
+      idbDatabases.length === 0, 'IndexedDB is empty.',
+      `IndexedDB is not empty. Found ${idbDatabases.length} databases.`);
+  const cacheKeys = await caches.keys();
+  check(
+      cacheKeys.length === 0, 'CacheStorage is empty.',
+      `CacheStorage is not empty. Found ${cacheKeys.length} caches.`);
+
+  const opfsEntries = await (async () => {
+    const entries = [];
+    for await (
+        const entry of (await navigator.storage.getDirectory()).values()) {
+      entries.push(entry.name);
+    }
+    return entries;
+  })();
+  check(
+      opfsEntries.length === 0, 'Origin Private File System is empty.',
+      `Origin Private File System is not empty.`);
+
+  return isOverallClean;
+}
+
+window.runStorageIsolationTest = async function(testFileName) {
+  console.log(`--- [${testFileName}] Starting Test ---`);
+  console.log('Verifying initial state...');
+  await verifyStorageIsEmptyAndLogDetails();
+
+  console.log('Creating data...');
+  await createAllData(testFileName);
+
+  console.log(`--- [${testFileName}] Test Finished ---`);
+};
+})();
diff --git a/third_party/blink/web_tests/http/tests/test-runner/storage-isolation-1-expected.txt b/third_party/blink/web_tests/http/tests/test-runner/storage-isolation-1-expected.txt
new file mode 100644
index 0000000..23a14839
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/test-runner/storage-isolation-1-expected.txt
@@ -0,0 +1,9 @@
+CONSOLE MESSAGE: --- [storage-isolation-1.html] Starting Test ---
+CONSOLE MESSAGE: Verifying initial state...
+CONSOLE MESSAGE:   OK: LocalStorage is empty.
+CONSOLE MESSAGE:   OK: IndexedDB is empty.
+CONSOLE MESSAGE:   OK: CacheStorage is empty.
+CONSOLE MESSAGE:   OK: Origin Private File System is empty.
+CONSOLE MESSAGE: Creating data...
+CONSOLE MESSAGE: --- [storage-isolation-1.html] Test Finished ---
+
diff --git a/third_party/blink/web_tests/http/tests/test-runner/storage-isolation-1.html b/third_party/blink/web_tests/http/tests/test-runner/storage-isolation-1.html
new file mode 100644
index 0000000..22f3950
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/test-runner/storage-isolation-1.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script>
+      if (window.testRunner) {
+        testRunner.waitUntilDone();
+        testRunner.dumpAsText();
+        testRunner.setDumpConsoleMessages(true);
+      }
+    </script>
+    <script src="resources/storage-isolation-test-util.js"></script>
+    <script>
+      window.onload = async function () {
+        await window.runStorageIsolationTest("storage-isolation-1.html");
+        if (window.testRunner) {
+          testRunner.notifyDone();
+        }
+      };
+    </script>
+  </head>
+  <body></body>
+</html>
diff --git a/third_party/blink/web_tests/http/tests/test-runner/storage-isolation-2-expected.txt b/third_party/blink/web_tests/http/tests/test-runner/storage-isolation-2-expected.txt
new file mode 100644
index 0000000..3343385
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/test-runner/storage-isolation-2-expected.txt
@@ -0,0 +1,9 @@
+CONSOLE MESSAGE: --- [storage-isolation-2.html] Starting Test ---
+CONSOLE MESSAGE: Verifying initial state...
+CONSOLE MESSAGE:   OK: LocalStorage is empty.
+CONSOLE MESSAGE:   OK: IndexedDB is empty.
+CONSOLE MESSAGE:   OK: CacheStorage is empty.
+CONSOLE MESSAGE:   OK: Origin Private File System is empty.
+CONSOLE MESSAGE: Creating data...
+CONSOLE MESSAGE: --- [storage-isolation-2.html] Test Finished ---
+
diff --git a/third_party/blink/web_tests/http/tests/test-runner/storage-isolation-2.html b/third_party/blink/web_tests/http/tests/test-runner/storage-isolation-2.html
new file mode 100644
index 0000000..97a9a7d5
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/test-runner/storage-isolation-2.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script>
+      if (window.testRunner) {
+        testRunner.waitUntilDone();
+        testRunner.dumpAsText();
+        testRunner.setDumpConsoleMessages(true);
+      }
+    </script>
+    <script src="resources/storage-isolation-test-util.js"></script>
+    <script>
+      window.onload = async function () {
+        await window.runStorageIsolationTest("storage-isolation-2.html");
+        if (window.testRunner) {
+          testRunner.notifyDone();
+        }
+      };
+    </script>
+  </head>
+  <body></body>
+</html>
diff --git a/third_party/blink/web_tests/inspector-protocol/sessions/page-set-document-content.js b/third_party/blink/web_tests/inspector-protocol/sessions/page-set-document-content.js
index a1db1c23..f6214fbc 100644
--- a/third_party/blink/web_tests/inspector-protocol/sessions/page-set-document-content.js
+++ b/third_party/blink/web_tests/inspector-protocol/sessions/page-set-document-content.js
@@ -3,9 +3,9 @@
   var page = await testRunner.createPage();
 
   var session1 = await page.createSession();
-  session1.protocol.Page.enable();
+  await session1.protocol.Page.enable();
   var session2 = await page.createSession();
-  session2.protocol.Page.enable();
+  await session2.protocol.Page.enable();
 
   var promise1 = session1.protocol.Page.onceFrameNavigated();
   var promise2 = session2.protocol.Page.onceFrameNavigated();
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/emoji/emoji-unicode17-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/emoji/emoji-unicode17-expected.png
new file mode 100644
index 0000000..e7f2551
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/emoji/emoji-unicode17-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13/virtual/css-anchor-update-enabled/external/wpt/css/css-anchor-position/anchor-scroll-position-try-014-expected.txt b/third_party/blink/web_tests/platform/mac-mac13/virtual/css-anchor-update-enabled/external/wpt/css/css-anchor-position/anchor-scroll-position-try-014-expected.txt
deleted file mode 100644
index 035410983..0000000
--- a/third_party/blink/web_tests/platform/mac-mac13/virtual/css-anchor-update-enabled/external/wpt/css/css-anchor-position/anchor-scroll-position-try-014-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] anchor-scroll-position-try-014 2
-  assert_equals: Anchored element should be at the bottom of anchor expected 109 but got 9
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/emoji/emoji-unicode17-expected.png b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/emoji/emoji-unicode17-expected.png
new file mode 100644
index 0000000..e7fe41d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/emoji/emoji-unicode17-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/text-antialias/emoji/emoji-unicode17-expected.png b/third_party/blink/web_tests/platform/win/virtual/text-antialias/emoji/emoji-unicode17-expected.png
new file mode 100644
index 0000000..a313a5f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/text-antialias/emoji/emoji-unicode17-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
index 844d8ae..3f78dd2 100644
--- a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
+++ b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
@@ -409,7 +409,6 @@
 text-wrap-mode: wrap
 text-wrap-style: auto
 timeline-scope: none
-timeline-trigger-behavior: once
 timeline-trigger-exit-range-end: auto
 timeline-trigger-exit-range-start: auto
 timeline-trigger-name: none
diff --git a/third_party/blink/web_tests/third_party/NotoColorEmoji/NotoColorEmoji.ttf b/third_party/blink/web_tests/third_party/NotoColorEmoji/NotoColorEmoji.ttf
index 3117123..943741d 100644
--- a/third_party/blink/web_tests/third_party/NotoColorEmoji/NotoColorEmoji.ttf
+++ b/third_party/blink/web_tests/third_party/NotoColorEmoji/NotoColorEmoji.ttf
Binary files differ
diff --git a/third_party/blink/web_tests/third_party/NotoColorEmoji/README.chromium b/third_party/blink/web_tests/third_party/NotoColorEmoji/README.chromium
index fe228b1..100427f1 100644
--- a/third_party/blink/web_tests/third_party/NotoColorEmoji/README.chromium
+++ b/third_party/blink/web_tests/third_party/NotoColorEmoji/README.chromium
@@ -1,7 +1,7 @@
 Name: Noto Color Emoji
 URL: https://github.com/googlei18n/noto-emoji
-Version: v2018-08-10-unicode11
-Revision: b00937512ed90839b9e7347e9e0f1b1abff62438
+Version: v2.051
+Revision: 8998f5dd683424a73e2314a8c1f1e359c19e8742
 Update Mechanism: Manual
 License: OFL-1.1
 License File: LICENSE
diff --git a/third_party/blink/web_tests/virtual/text-antialias/emoji/emoji-unicode17.html b/third_party/blink/web_tests/virtual/text-antialias/emoji/emoji-unicode17.html
new file mode 100644
index 0000000..3ec320cbe
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/text-antialias/emoji/emoji-unicode17.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<style>
+@font-face {
+  font-family: 'Noto Color Emoji';
+  font-style: normal;
+  font-weight: 400;
+  font-display: swap;
+  src: url("../../../third_party/NotoColorEmoji/NotoColorEmoji.ttf");
+}
+
+ol {
+  font-family: sans-serif;
+  font-size: 40px;
+  margin-block-start: 0;
+}
+
+.noto-color-emoji {
+  font-family: 'Noto Color Emoji', sans-serif;
+
+  li::marker {
+    font-family: sans-serif;
+  }
+}
+</style>
+
+<ol class="noto-color-emoji">
+  <li>&#x1F6D8;</li>
+  <li>&#x1FA8A;</li>
+  <li>&#x1FA8E;</li>
+  <li>&#x1FAC8;</li>
+  <li>&#x1FACD;</li>
+  <li>&#x1FAEA;</li>
+  <li>&#x1FAEF;</li>
+</ol>
diff --git a/third_party/blink/web_tests/virtual/unload-allowed/external/wpt/navigation-api/ordering-and-transition/back-cross-document-event-order-expected.txt b/third_party/blink/web_tests/virtual/unload-allowed/external/wpt/navigation-api/ordering-and-transition/back-cross-document-event-order-expected.txt
new file mode 100644
index 0000000..d2490db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/unload-allowed/external/wpt/navigation-api/ordering-and-transition/back-cross-document-event-order-expected.txt
@@ -0,0 +1,3 @@
+This is a testharness.js-based test.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/unload-allowed/external/wpt/navigation-api/ordering-and-transition/navigate-cross-document-event-order-expected.txt b/third_party/blink/web_tests/virtual/unload-allowed/external/wpt/navigation-api/ordering-and-transition/navigate-cross-document-event-order-expected.txt
new file mode 100644
index 0000000..d2490db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/unload-allowed/external/wpt/navigation-api/ordering-and-transition/navigate-cross-document-event-order-expected.txt
@@ -0,0 +1,3 @@
+This is a testharness.js-based test.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
index 1ac6c10..df9adfa 100644
--- a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
@@ -536,7 +536,6 @@
 textWrapStyle
 timelineScope
 timelineTrigger
-timelineTriggerBehavior
 timelineTriggerExitRangeEnd
 timelineTriggerExitRangeStart
 timelineTriggerName
diff --git a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
index 82be5ed..ad9055e 100644
--- a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
@@ -442,7 +442,6 @@
     text-wrap-mode
     text-wrap-style
     timeline-scope
-    timeline-trigger-behavior
     timeline-trigger-exit-range-end
     timeline-trigger-exit-range-start
     timeline-trigger-name
@@ -929,7 +928,6 @@
         text-wrap-mode
         text-wrap-style
     timeline-trigger
-        timeline-trigger-behavior
         timeline-trigger-exit-range-end
         timeline-trigger-exit-range-start
         timeline-trigger-name
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 62ffacd..e8ba040e 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -1578,6 +1578,7 @@
 [Worker]     setter textRendering
 [Worker]     setter wordSpacing
 [Worker] interface Origin
+[Worker]     static method from
 [Worker]     static method fromURL
 [Worker]     static method parse
 [Worker]     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 91d78d2..940266b 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -7199,6 +7199,7 @@
     method constructor
     method populateMatrix
 interface Origin
+    static method from
     static method fromURL
     static method parse
     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index e186e24..4ecff831 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -1413,6 +1413,7 @@
 [Worker]     setter textRendering
 [Worker]     setter wordSpacing
 [Worker] interface Origin
+[Worker]     static method from
 [Worker]     static method fromURL
 [Worker]     static method parse
 [Worker]     attribute @@toStringTag
diff --git a/third_party/blink/web_tests/wpt_internal/ai/language-model-api-prompt-multimodal.https.window.js b/third_party/blink/web_tests/wpt_internal/ai/language-model-api-prompt-multimodal.https.window.js
index 793f562..553b06e 100644
--- a/third_party/blink/web_tests/wpt_internal/ai/language-model-api-prompt-multimodal.https.window.js
+++ b/third_party/blink/web_tests/wpt_internal/ai/language-model-api-prompt-multimodal.https.window.js
@@ -10,7 +10,7 @@
 const kValidImagePath = 'resources/media/apple.jpg';
 const kValidAudioPath = 'resources/media/speech.mp3';
 const kValidSVGImagePath = 'resources/media/wikipedia-logo.svg';
-const kValidVideoPath = 'resources/media/video.mp4';
+const kValidVideoPath = 'resources/media/video.webm';
 
 const kImageOptions = {expectedInputs: [{type: 'image'}]};
 const kAudioOptions = {expectedInputs: [{type: 'audio'}]};
diff --git a/third_party/blink/web_tests/wpt_internal/ai/resources/media/video.mp4 b/third_party/blink/web_tests/wpt_internal/ai/resources/media/video.mp4
deleted file mode 100644
index ed139d6d..0000000
--- a/third_party/blink/web_tests/wpt_internal/ai/resources/media/video.mp4
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/wpt_internal/ai/resources/media/video.webm b/third_party/blink/web_tests/wpt_internal/ai/resources/media/video.webm
new file mode 100644
index 0000000..f59dbd68
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/ai/resources/media/video.webm
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/item-placement/dense-packing/column-dense-packing-multi-span-005-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-dense-packing-multi-span-005-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/item-placement/dense-packing/column-dense-packing-multi-span-005-ref.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/column-dense-packing-multi-span-005-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/item-placement/dense-packing/column-dense-packing-multi-span-005.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-dense-packing-multi-span-005.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-grid/masonry/tentative/item-placement/dense-packing/column-dense-packing-multi-span-005.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/column-dense-packing-multi-span-005.html
diff --git a/third_party/boringssl/src b/third_party/boringssl/src
index 88d0c0f..1ae7468 160000
--- a/third_party/boringssl/src
+++ b/third_party/boringssl/src
@@ -1 +1 @@
-Subproject commit 88d0c0f4772f3abe74f4f1012fe580fa85bab417
+Subproject commit 1ae74684a023cccf8d9e6d478e6ea25cd37f7eac
diff --git a/third_party/breakpad/README.chromium b/third_party/breakpad/README.chromium
index 4a74b44..5ba6afd13 100644
--- a/third_party/breakpad/README.chromium
+++ b/third_party/breakpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: breakpad
 URL: https://chromium.googlesource.com/breakpad/breakpad
 Version: N/A
-Revision: 884742b238232b434fa03a284372ee3b5bb96a08
+Revision: 85e647e1feb9599427a8fb1e3cd563de44d76557
 Update Mechanism: Manual
 License: BSD-3-Clause, cURL, APSL-2.0, Apache-2.0
 License File: LICENSE
diff --git a/third_party/breakpad/breakpad b/third_party/breakpad/breakpad
index 8b96cc5d..85e647e 160000
--- a/third_party/breakpad/breakpad
+++ b/third_party/breakpad/breakpad
@@ -1 +1 @@
-Subproject commit 8b96cc5d7a809bed83d7a0eeaaba6a5c3133a6f4
+Subproject commit 85e647e1feb9599427a8fb1e3cd563de44d76557
diff --git a/third_party/catapult b/third_party/catapult
index 4ca50c8..f724a59 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit 4ca50c8ae1cea0c8f2c87eae90c0fbcb6d7e262b
+Subproject commit f724a59d02db629831e1b4b3eff407cec902f49b
diff --git a/third_party/closure_compiler/externs/login_state.js b/third_party/closure_compiler/externs/login_state.js
index b117240..c148fda 100644
--- a/third_party/closure_compiler/externs/login_state.js
+++ b/third_party/closure_compiler/externs/login_state.js
@@ -24,6 +24,7 @@
 chrome.loginState.ProfileType = {
   SIGNIN_PROFILE: 'SIGNIN_PROFILE',
   USER_PROFILE: 'USER_PROFILE',
+  LOCK_PROFILE: 'LOCK_PROFILE',
 };
 
 /**
diff --git a/third_party/compiler-rt/src b/third_party/compiler-rt/src
index b213d0f..dda6158 160000
--- a/third_party/compiler-rt/src
+++ b/third_party/compiler-rt/src
@@ -1 +1 @@
-Subproject commit b213d0f0f24a2f1f682e4712b75f094f34c9c0e7
+Subproject commit dda6158cb0a75b2ef6534e522c9dc20368ef617f
diff --git a/third_party/crossbench b/third_party/crossbench
index 414b4b8..fc2b2ed 160000
--- a/third_party/crossbench
+++ b/third_party/crossbench
@@ -1 +1 @@
-Subproject commit 414b4b87f2225616eecd5bca5025fc2e4ee6134c
+Subproject commit fc2b2ed17dceee8def2ce9fd3135cc0b7efb215e
diff --git a/third_party/dawn b/third_party/dawn
index 43d8fb0..d952ec4 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit 43d8fb0293947a3fec5c9ba187f16a9176597d31
+Subproject commit d952ec49e3d4b719c7196cfcb7d2a25b6c8b070d
diff --git a/third_party/depot_tools b/third_party/depot_tools
index eda3b48..88b3c4e 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit eda3b48f2e32304d4ea20fee7ac1d37c9b060b3b
+Subproject commit 88b3c4e0b1f356fce125752dbac35f9745767e0d
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 6bce091..fa14551 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 6bce0915c43dcb44304db70d05e52bcc35a096c6
+Subproject commit fa145518edf09e5c69c7ffb42b7aebcb6d1f3ece
diff --git a/third_party/fuzztest/src b/third_party/fuzztest/src
index 0309379..f13e4d8 160000
--- a/third_party/fuzztest/src
+++ b/third_party/fuzztest/src
@@ -1 +1 @@
-Subproject commit 03093791de854345c32ce345f6b6a32b822ac940
+Subproject commit f13e4d833697557712f43f06ecb2e1e3b874801f
diff --git a/third_party/glslang/src b/third_party/glslang/src
index a57276b..f6652dc 160000
--- a/third_party/glslang/src
+++ b/third_party/glslang/src
@@ -1 +1 @@
-Subproject commit a57276bf558f5cf94d3a9854ebdf5a2236849a5a
+Subproject commit f6652dcf751920b1fbc132619b0e84ef3d6e77c4
diff --git a/third_party/libc++/src b/third_party/libc++/src
index cc5f928..4b63891 160000
--- a/third_party/libc++/src
+++ b/third_party/libc++/src
@@ -1 +1 @@
-Subproject commit cc5f928de999c9c86202d7eb1d0f8bdc3b53465f
+Subproject commit 4b6389141910f2bb5df4574b6e0e9ec5a1acb165
diff --git a/third_party/libwebm/METADATA b/third_party/libwebm/METADATA
new file mode 100644
index 0000000..4cc29300
--- /dev/null
+++ b/third_party/libwebm/METADATA
@@ -0,0 +1,8 @@
+third_party {
+  security {
+    # This CVE has an overly broad 'Known Affected' list. The issue was fixed
+    # in libwebm-1.0.0.27-334-g8e88e04. The affected code is not included in
+    # libwebm or Chrome.
+    mitigated_security_patch: "CVE-2018-6406"
+  }
+}
diff --git a/third_party/llvm-libc/src b/third_party/llvm-libc/src
index afad4d4..b53ec82 160000
--- a/third_party/llvm-libc/src
+++ b/third_party/llvm-libc/src
@@ -1 +1 @@
-Subproject commit afad4d47e760ddc610e27653719192415b663798
+Subproject commit b53ec82f9c6f262bae65028e981f9ba35e612642
diff --git a/third_party/omnibox_proto/README.chromium b/third_party/omnibox_proto/README.chromium
index 3697ab8..cddbe1ce9 100644
--- a/third_party/omnibox_proto/README.chromium
+++ b/third_party/omnibox_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Omnibox Protos
 Short Name: omnibox_proto
 URL: This is the canonical public repository
-Version: 812877622
-Date: 2025-09-29
+Version: 815903419
+Date: 2025-10-06
 License: BSD-3-Clause
 License File: LICENSE
 Shipped: yes
diff --git a/third_party/omnibox_proto/aim_eligibility_response.proto b/third_party/omnibox_proto/aim_eligibility_response.proto
index 55ad6b2..e8c70fb 100644
--- a/third_party/omnibox_proto/aim_eligibility_response.proto
+++ b/third_party/omnibox_proto/aim_eligibility_response.proto
@@ -11,10 +11,11 @@
 package omnibox;
 
 // AIM Eligibility Response.
-// Next ID: 5
+// Next ID: 6
 message AimEligibilityResponse {
   optional bool is_eligible = 1;
   optional bool is_pdf_upload_eligible = 2;
   optional bool is_deep_search_eligible = 3;
   optional bool is_canvas_eligible = 4;
+  optional int32 session_index = 5;
 }
diff --git a/third_party/openscreen/src b/third_party/openscreen/src
index 4654d79..efc3a38 160000
--- a/third_party/openscreen/src
+++ b/third_party/openscreen/src
@@ -1 +1 @@
-Subproject commit 4654d7968ac1a1e825a84bd4d85acadd7993c49a
+Subproject commit efc3a38d81db2ec422035b221c0e90ec376ac8df
diff --git a/third_party/pdfium b/third_party/pdfium
index 4fdc024..82dca10 160000
--- a/third_party/pdfium
+++ b/third_party/pdfium
@@ -1 +1 @@
-Subproject commit 4fdc024595ef291b63d0f3219ee66ce286018ea1
+Subproject commit 82dca105b0c782cf9aa1ae7098b62b8c47173562
diff --git a/third_party/perfetto b/third_party/perfetto
index b6e6c4d..d57eda6 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit b6e6c4d600d07e3974db4db36e0010291ce49441
+Subproject commit d57eda6f192a4d5f147531d3f63355b5e36c5c78
diff --git a/third_party/search_engines_data/resources b/third_party/search_engines_data/resources
index d5532f8..4de7ff6 160000
--- a/third_party/search_engines_data/resources
+++ b/third_party/search_engines_data/resources
@@ -1 +1 @@
-Subproject commit d5532f82099d33fb441b936ac9ecf832d275a3fa
+Subproject commit 4de7ff619acb3a46e43238a05a6ca981d7680e1c
diff --git a/third_party/search_engines_data/resources_internal b/third_party/search_engines_data/resources_internal
index b7e5ea3..0547106 160000
--- a/third_party/search_engines_data/resources_internal
+++ b/third_party/search_engines_data/resources_internal
@@ -1 +1 @@
-Subproject commit b7e5ea3247cdce918cedf569c35a736bf6affda4
+Subproject commit 0547106e1f3f50a4b7e3f21ab4447ebf8ccf5eab
diff --git a/third_party/skia b/third_party/skia
index 5479115..cc4a8b9 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 5479115ef5bfc73266e94e69459da8e1da872fbe
+Subproject commit cc4a8b9e5c69f182ea856bb4e940068674eff0fe
diff --git a/third_party/swiftshader b/third_party/swiftshader
index 18d4f3d..794b0cf 160000
--- a/third_party/swiftshader
+++ b/third_party/swiftshader
@@ -1 +1 @@
-Subproject commit 18d4f3db94079d05896692b9c6b3d905ec84bce7
+Subproject commit 794b0cfce1d828d187637e6d932bae484fbe0976
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 207fff6..aa21164 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 207fff6486dbecfe236dd34ea206d5fc10a1f74f
+Subproject commit aa211644ec30b826d9bac4d80d90efe794f94733
diff --git a/third_party/vulkan-headers/src b/third_party/vulkan-headers/src
index a4f8ada..4eebce1 160000
--- a/third_party/vulkan-headers/src
+++ b/third_party/vulkan-headers/src
@@ -1 +1 @@
-Subproject commit a4f8ada9f4f97c45b8c89c57997be9cebaae65d2
+Subproject commit 4eebce1723aced9afa272c38dfd4f82a786ed9ad
diff --git a/third_party/vulkan-loader/src b/third_party/vulkan-loader/src
index ae0461b..385716f 160000
--- a/third_party/vulkan-loader/src
+++ b/third_party/vulkan-loader/src
@@ -1 +1 @@
-Subproject commit ae0461b671558197a9a50e5fcfcc3b2d3f406b42
+Subproject commit 385716f0a63fe2c26e54a5140c9877a80da66592
diff --git a/third_party/vulkan-tools/src b/third_party/vulkan-tools/src
index 5568ce1..8ebb2a6 160000
--- a/third_party/vulkan-tools/src
+++ b/third_party/vulkan-tools/src
@@ -1 +1 @@
-Subproject commit 5568ce14705e512113df5b459fc86d857b3d7789
+Subproject commit 8ebb2a62f5beee9447a8899a57db291d4034b0fc
diff --git a/third_party/vulkan-utility-libraries/src b/third_party/vulkan-utility-libraries/src
index 4d0b838..a1e4594 160000
--- a/third_party/vulkan-utility-libraries/src
+++ b/third_party/vulkan-utility-libraries/src
@@ -1 +1 @@
-Subproject commit 4d0b838ffcf1ef81151f0e7e11fad1d9ff859813
+Subproject commit a1e45945b3a84140956dc4672684090cf8e636a4
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src
index 9657ea8..270dbea 160000
--- a/third_party/vulkan-validation-layers/src
+++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@
-Subproject commit 9657ea8efe117f6b852dbf9974d6f77812880713
+Subproject commit 270dbea039c2b485e33330be58a3e0ebcaef485f
diff --git a/third_party/wayland/README.chromium b/third_party/wayland/README.chromium
index eb7cca2..fac6dc2 100644
--- a/third_party/wayland/README.chromium
+++ b/third_party/wayland/README.chromium
@@ -1,6 +1,6 @@
 Name: wayland
 URL: http://wayland.freedesktop.org/
-Version: 1.23.0
+Version: 1.24.0
 Update Mechanism: Manual
 CPEPrefix: cpe:/a:wayland:wayland:1.20.91
 License: MIT
@@ -14,7 +14,7 @@
 
 Note: It appears that Wayland has been failing to register CPEs with
 nvd.nist.gov. Thus the last actual CPEPrefix is 1.20.91, even though the latest
-version is 1.23.0.
+version is 1.24.0.
 
 Modifications:
 - Added wayland_scanner_wrapper.py to generate protocol code automatically.
diff --git a/third_party/wayland/include/config.h b/third_party/wayland/include/config.h
index 6c2caad..6386aa1 100644
--- a/third_party/wayland/include/config.h
+++ b/third_party/wayland/include/config.h
@@ -31,4 +31,5 @@
 
 #define PACKAGE "wayland"
 
-#define PACKAGE_VERSION "1.23.0"
+#define PACKAGE_VERSION "1.24.0"
+
diff --git a/third_party/wayland/include/src/wayland-version.h b/third_party/wayland/include/src/wayland-version.h
index 0bc6601..dbda6b8 100644
--- a/third_party/wayland/include/src/wayland-version.h
+++ b/third_party/wayland/include/src/wayland-version.h
@@ -27,8 +27,8 @@
 #define WAYLAND_VERSION_H
 
 #define WAYLAND_VERSION_MAJOR 1
-#define WAYLAND_VERSION_MINOR 23
+#define WAYLAND_VERSION_MINOR 24
 #define WAYLAND_VERSION_MICRO 0
-#define WAYLAND_VERSION "1.23.0"
+#define WAYLAND_VERSION "1.24.0"
 
 #endif
diff --git a/third_party/wayland/src b/third_party/wayland/src
index a156431..736d12a 160000
--- a/third_party/wayland/src
+++ b/third_party/wayland/src
@@ -1 +1 @@
-Subproject commit a156431ea66fe67d69c9fbba8a8ad34dabbab81c
+Subproject commit 736d12ac67c20c60dc406dc49bb06be878501f86
diff --git a/third_party/wayland/wayland_version.gni b/third_party/wayland/wayland_version.gni
index 90bb80af4..3789339 100644
--- a/third_party/wayland/wayland_version.gni
+++ b/third_party/wayland/wayland_version.gni
@@ -4,5 +4,5 @@
 # is true to check system wayland package version meets
 # at least the version of third-party/wayland so that it won't
 # make any compile error with chromium
-wayland_version = "1.23.0"
+wayland_version = "1.24.0"
 wayland_egl_version = "18.1.0"
diff --git a/third_party/webrtc b/third_party/webrtc
index b0db25b..06fd850 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit b0db25b10ced338a04ce88106ed3e693cca96602
+Subproject commit 06fd850ea8e86ed78cf341d5f989b8ccbbd3266f
diff --git a/tools/android/avd/avd.py b/tools/android/avd/avd.py
index e533d33a..f34294a 100755
--- a/tools/android/avd/avd.py
+++ b/tools/android/avd/avd.py
@@ -4,10 +4,12 @@
 # found in the LICENSE file.
 
 import argparse
+import dataclasses
 import os
 import logging
 import json
 import pathlib
+import signal
 import sys
 
 _SRC_ROOT = os.path.abspath(
@@ -16,6 +18,7 @@
 sys.path.append(
     os.path.join(_SRC_ROOT, 'third_party', 'catapult', 'devil'))
 from devil.android.tools import script_common
+from devil.android.sdk import adb_wrapper
 from devil.utils import logging_common
 
 sys.path.append(
@@ -23,6 +26,41 @@
 import devil_chromium
 from pylib.local.emulator import avd
 
+# From .vpython
+import psutil
+import tabulate
+
+
+@dataclasses.dataclass(frozen=True)
+class _Process:
+  pid: int
+  port: int
+  cmd: str
+
+
+def _detect_emulator_processes():
+  serials = [adb.GetDeviceSerial() for adb in adb_wrapper.AdbWrapper.Devices()]
+  emulator_ports = {
+      int(s.split('-')[-1])
+      for s in serials if s.startswith('emulator-')
+  }
+  if not emulator_ports:
+    return []
+  found = {(p.pid, p.laddr.port)
+           for p in psutil.net_connections()
+           if p.status == psutil.CONN_LISTEN and p.laddr.port in emulator_ports}
+  return [
+      _Process(x[0], x[1],
+               psutil.Process(x[0]).cmdline()[0]) for x in found
+  ]
+
+
+def _avd_procs_for_config(path, avd_procs):
+  # Example: /usr/local/google/code/clankium1/src/.android_emulator/android_34_google_apis_x64_local/emulator/qemu/linux-x86_64/qemu-system-x86_64
+  avd_name = os.path.basename(path).removesuffix('.textpb')
+  key = f'{avd_name}{os.path.sep}'
+  return [p for p in avd_procs if key in p.cmd]
+
 
 def _add_avd_config_argument(parser, required=True):
   parser.add_argument('--avd-config',
@@ -252,19 +290,57 @@
       print('No avd config files found.')
       return 0
 
+    avd_procs = _detect_emulator_processes()
+
     avd_configs = [avd.AvdConfig(os.path.relpath(f)) for f in sorted(files)]
     metadata = [config.GetMetadata() for config in avd_configs]
+    for row in metadata:
+      cur_avd_procs = _avd_procs_for_config(row['avd_proto_path'], avd_procs)
+      row['active_pids'] = ', '.join(str(p.pid) for p in cur_avd_procs)
+      row['active_serials'] = ', '.join(f'emulator-{p.port}'
+                                        for p in cur_avd_procs)
     if args.json_output:
       with open(args.json_output, 'w') as json_file:
         json.dump(metadata, json_file, indent=2)
     else:
       # Import tabulate only when needed, in case it is not listed in .vpython3.
-      tabulate = __import__('tabulate')
       print(tabulate.tabulate(metadata, headers='keys'))
     return 0
 
   subparser.set_defaults(func=list_cmd)
 
+  subparser = subparsers.add_parser(
+      'stop',
+      help='Stops emulators for the given avd config (or all emulators if no '
+      'config is given)',
+      formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+  _add_common_arguments(subparser)
+  _add_avd_config_argument(subparser, required=False)
+
+  def stop_cmd(args):
+    avd_procs = _detect_emulator_processes()
+
+    if args.avd_config:
+      avd_procs = _avd_procs_for_config(args.avd_config, avd_procs)
+      if not avd_procs:
+        print('No emulators found for avd config:', args.avd_config)
+        return
+    elif not avd_procs:
+      print('No emulators found.')
+      return
+
+    for proc in avd_procs:
+      os.kill(proc.pid, signal.SIGINT)
+
+    print(f'Sent SIGINT to {len(avd_procs)} emulator(s).')
+    for proc in avd_procs:
+      try:
+        psutil.Process(proc.pid).wait()
+      except psutil.NoSuchProcess:
+        pass
+
+  subparser.set_defaults(func=stop_cmd)
+
   if len(sys.argv) == 1:
     parser.print_help()
     return 1
diff --git a/tools/clang/plugins/UnsafeBuffersPlugin.cpp b/tools/clang/plugins/UnsafeBuffersPlugin.cpp
index 75c9892..61f00724 100644
--- a/tools/clang/plugins/UnsafeBuffersPlugin.cpp
+++ b/tools/clang/plugins/UnsafeBuffersPlugin.cpp
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <algorithm>
+
 #include "Util.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/Basic/DiagnosticSema.h"
@@ -26,12 +28,172 @@
 // determine from the path prefixes control file.
 llvm::StringMap<Disposition> g_checked_files_cache;
 
-struct CheckFilePrefixes {
-  // `buffer` owns the memory for the strings in `prefix_map`.
-  std::unique_ptr<llvm::MemoryBuffer> buffer;
-  std::map<llvm::StringRef, char> prefix_map;
+namespace {
+
+bool ContainsBackslash(const llvm::SmallVector<char>& path) {
+  return std::find(path.begin(), path.end(), '\\') != path.end();
+}
+
+}  // namespace
+
+class UnsafeBuffersConfig {
+ public:
+  bool LoadFromPath(std::string path) {
+    // Parse the contents of the path suppression file.
+    //
+    // The file format is as follows:
+    // * `#` introduces a comment until the end of the line.
+    // * Empty lines are ignored.
+    // * Active lines consist of a one-character command followed by data.
+    // * A line beginning with a `.` lists diagnostics to enable. These
+    //   are comma-separated and currently allow: `buffers`, `libc`.
+    // * A line beginning with a `$` gives the path from this file to
+    //   the source tree root. Path prefixes in this file are interpreted
+    //   relative to this value (default is that this file is in the root).
+    // * A line beginning with ''@' is silently ignored (reserved for
+    //   rolling in future features without breakage).
+    // * Every other line is a path prefix from the source tree root using
+    //   unix-style delimiters:
+    //   * Each line either removes a path from checks or adds a path to checks.
+    //   * If the line starts with `+` paths matching the line will be added.
+    //   * If the line starts with `-` paths matching the line will removed.
+    //   * Other starting characters are not allowed.
+    //   * Paths naming directories match the entire sub-directory. For instance
+    //     `+a/b/` will match the file at `//a/b/c.h` but will *not* match
+    //     `//other/a/b/c.h`.
+    //   * Paths naming files match the single file and look like `+a/b/c.h`.
+    //   * Trailing slashes for directories are recommended, but not enforced.
+    //   * The longest (most specific) match takes precedence.
+    //   * Files that do not match any of the prefixes file will be checked.
+    //   * Duplicate prefixes are not allowed and produce compilation errors.
+    //
+    // Example:
+    // ```
+    // # A file of path prefixes.
+    // # Matches anything under the directory //foo/bar, opting them into
+    // # checks.
+    // +foo/bar/
+    // # Avoids checks in the //my directory.
+    // -my/
+    // # Matches a specific file at //my/file.cc, overriding the `-my/` above
+    // # for this one file.
+    // +my/file.cc
+    // ```
+    //
+    if (auto buffer_or_error = llvm::MemoryBuffer::getFileAsStream(path)) {
+      buffer_ = std::move(buffer_or_error.get());
+    } else {
+      llvm::errs() << "[unsafe-buffers] Error reading file: '"
+                   << buffer_or_error.getError().message() << "'\n";
+      return false;
+    }
+
+    llvm::replace(path, '\\', '/');
+    path_to_source_root_.assign(path.begin(), path.end());
+    llvm::sys::path::remove_filename(path_to_source_root_,
+                                     llvm::sys::path::Style::posix);
+    if (!path_to_source_root_.empty() && path_to_source_root_.back() != '/') {
+      path_to_source_root_.append(1, '/');
+    }
+
+    llvm::StringRef string = buffer_->getBuffer();
+    while (!string.empty()) {
+      auto [line, remainder] = string.split('\n');
+      string = remainder;
+      auto [active, comment] = line.split('#');
+      active = active.trim();
+      if (active.empty()) {
+        continue;
+      }
+      char symbol = active[0u];
+      llvm::StringRef operand = active.substr(1u);
+      if (symbol == '.') {
+        // A "dot" line contains directives to enable.
+        if (operand.contains("buffers")) {
+          check_buffers = true;
+        }
+        if (operand.contains("libc")) {
+          check_libc_calls = true;
+        }
+        continue;
+      }
+      if (symbol == '$') {
+        // A "dollar" line contains path adjustment to find source root.
+        path_to_source_root_.append(operand.begin(), operand.end());
+        llvm::sys::path::remove_dots(path_to_source_root_, true,
+                                     llvm::sys::path::Style::posix);
+        path_to_source_root_.append(1, '/');
+        continue;
+      }
+      if (symbol == '@') {
+        // Reserved for future expansion, file inclusion.
+        continue;
+      }
+      if (symbol != '+' && symbol != '-') {
+        llvm::errs() << "[unsafe-buffers] Invalid line in paths file, must "
+                     << "start with +/-: '" << line << "'\n";
+        return false;
+      }
+      llvm::StringRef prefix = operand.rtrim('/');
+      if (prefix.empty()) {
+        llvm::errs() << "[unsafe-buffers] Invalid line in paths file, path "
+                     << "must immediately follow +/-: '" << line << "'\n";
+        return false;
+      }
+      auto [ignore, was_inserted] = prefix_map_.insert({prefix, symbol});
+      if (!was_inserted) {
+        llvm::errs() << "[unsafe-buffers] Duplicate entry in paths file "
+                        "for '"
+                     << line << "'\n";
+        return false;
+      }
+    }
+    if (ContainsBackslash(path_to_source_root_)) {
+      llvm::errs()
+          << "[unsafe-buffers] Fatal plugin error, bad path format, see "
+          << __FILE__ << ":" << __LINE__ << "\n";
+      return false;
+    }
+    return true;
+  }
+
+  Disposition ShouldCheck(llvm::StringRef cmp_filename) {
+    // Remove any leading ./ prefix.
+    while (cmp_filename.consume_front("./")) {
+      continue;
+    }
+
+    // Resolve the ../../ prefixes.
+    llvm::StringRef path_to_source_ref(path_to_source_root_.data(),
+                                       path_to_source_root_.size());
+    if (!cmp_filename.consume_front(path_to_source_ref)) {
+      llvm::errs() << "[unsafe-buffers] file '" << cmp_filename
+                   << "' bad test against path '" << path_to_source_ref
+                   << "'\n";
+      return kSkip;
+    }
+
+    // Find the longest prefix match.
+    while (!cmp_filename.empty()) {
+      auto it = prefix_map_.find(cmp_filename);
+      if (it != prefix_map_.end()) {
+        return it->second == '+' ? kCheck : kSkip;
+      }
+      cmp_filename = llvm::sys::path::parent_path(
+          cmp_filename, llvm::sys::path::Style::posix);
+    }
+
+    return kCheck;
+  }
+
   bool check_buffers = true;
   bool check_libc_calls = false;
+
+ private:
+  // `buffer` owns the memory for the strings in `prefix_map_`.
+  std::unique_ptr<llvm::MemoryBuffer> buffer_;
+  std::map<llvm::StringRef, char> prefix_map_;
+  llvm::SmallVector<char> path_to_source_root_;
 };
 
 class UnsafeBuffersDiagnosticConsumer : public clang::DiagnosticConsumer {
@@ -39,11 +201,11 @@
   UnsafeBuffersDiagnosticConsumer(clang::DiagnosticsEngine* engine,
                                   clang::DiagnosticConsumer* next,
                                   clang::CompilerInstance* instance,
-                                  CheckFilePrefixes check_file_prefixes)
+                                  UnsafeBuffersConfig config)
       : engine_(engine),
         next_(next),
         instance_(instance),
-        check_file_prefixes_(std::move(check_file_prefixes)),
+        unsafe_buffers_config_(std::move(config)),
         diag_note_link_(engine_->getCustomDiagID(
             clang::DiagnosticsEngine::Level::Note,
             "See //docs/unsafe_buffers.md for help.")) {}
@@ -125,16 +287,16 @@
         diag_id == clang::diag::note_unsafe_buffer_printf_call;
 
     const bool ignore_diagnostic =
-        (is_buffers_diagnostic && !check_file_prefixes_.check_buffers) ||
-        (is_libc_diagnostic && !check_file_prefixes_.check_libc_calls);
+        (is_buffers_diagnostic && !unsafe_buffers_config_.check_buffers) ||
+        (is_libc_diagnostic && !unsafe_buffers_config_.check_libc_calls);
 
     if (ignore_diagnostic) {
       return;
     }
 
     const bool handle_diagnostic =
-        (is_buffers_diagnostic && check_file_prefixes_.check_buffers) ||
-        (is_libc_diagnostic && check_file_prefixes_.check_libc_calls);
+        (is_buffers_diagnostic && unsafe_buffers_config_.check_buffers) ||
+        (is_libc_diagnostic && unsafe_buffers_config_.check_libc_calls);
 
     if (!handle_diagnostic) {
       return PassthroughDiagnostic(level, diag);
@@ -239,7 +401,7 @@
     std::string filename = GetFilename(sm, loc, FilenameLocationType::kExactLoc,
                                        FilenamesFollowPresumed::kNo);
 
-    // Avoid searching `check_file_prefixes_` more than once for a file.
+    // Avoid searching `unsafe_buffers_config_` more than once for a file.
     auto cache_it = g_checked_files_cache.find(filename);
     if (cache_it != g_checked_files_cache.end()) {
       return cache_it->second;
@@ -258,20 +420,8 @@
       }
     }
 
-    // Drop the ../ prefixes.
-    while (cmp_filename.consume_front("./") ||
-           cmp_filename.consume_front("../"))
-      continue;
-
-    Disposition should_check = kCheck;
-    while (!cmp_filename.empty()) {
-      auto it = check_file_prefixes_.prefix_map.find(cmp_filename);
-      if (it != check_file_prefixes_.prefix_map.end()) {
-        should_check = it->second == '+' ? kCheck : kSkip;
-        break;
-      }
-      cmp_filename = llvm::sys::path::parent_path(cmp_filename);
-    }
+    // Check and cache results.
+    Disposition should_check = unsafe_buffers_config_.ShouldCheck(cmp_filename);
     g_checked_files_cache.insert({filename, should_check});
     return should_check;
   }
@@ -298,14 +448,14 @@
   clang::DiagnosticsEngine* engine_;
   clang::DiagnosticConsumer* next_;
   clang::CompilerInstance* instance_;
-  CheckFilePrefixes check_file_prefixes_;
+  UnsafeBuffersConfig unsafe_buffers_config_;
   unsigned diag_note_link_;
 };
 
 class UnsafeBuffersASTConsumer : public clang::ASTConsumer {
  public:
   UnsafeBuffersASTConsumer(clang::CompilerInstance* instance,
-                           CheckFilePrefixes check_file_prefixes)
+                           UnsafeBuffersConfig path_file_config)
       : instance_(instance) {
     // Replace the DiagnosticConsumer with our own that sniffs diagnostics and
     // can omit them.
@@ -314,7 +464,7 @@
     old_owned_client_ = engine.takeClient();
     engine.setClient(
         new UnsafeBuffersDiagnosticConsumer(&engine, old_client_, instance_,
-                                            std::move(check_file_prefixes)),
+                                            std::move(path_file_config)),
         /*owned=*/true);
 
     // Enable the -Wunsafe-buffer-usage warning as a remark. This prevents it
@@ -356,13 +506,13 @@
   std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
       clang::CompilerInstance& instance,
       llvm::StringRef ref) override {
-    assert(!moved_prefixes_);  // This would mean we move the prefixes twice.
-    moved_prefixes_ = true;
+    assert(!moved_config_);  // This would mean we move the config twice.
+    moved_config_ = true;
 
     // The ASTConsumer can outlive `this`, so we can't give it references to
-    // members here and must move the `check_file_prefixes_` vector instead.
+    // members here and must move the `unsafe_buffers_config_` vector instead.
     return std::make_unique<UnsafeBuffersASTConsumer>(
-        &instance, std::move(check_file_prefixes_));
+        &instance, std::move(unsafe_buffers_config_));
   }
 
   bool ParseArgs(const clang::CompilerInstance& instance,
@@ -381,7 +531,7 @@
 
       // Anything not recognized as a switch is the unsafe buffer paths file.
       found_file_arg = true;
-      if (!LoadCheckFilePrefixes(arg)) {
+      if (!unsafe_buffers_config_.LoadFromPath(arg)) {
         llvm::errs() << "[unsafe-buffers] Failed to load paths from file '"
                      << arg << "'\n";
         return false;
@@ -390,107 +540,9 @@
     return true;
   }
 
-  bool LoadCheckFilePrefixes(std::string_view path) {
-    if (auto buffer = llvm::MemoryBuffer::getFileAsStream(path)) {
-      check_file_prefixes_.buffer = std::move(buffer.get());
-    } else {
-      llvm::errs() << "[unsafe-buffers] Error reading file: '"
-                   << buffer.getError().message() << "'\n";
-      return false;
-    }
-
-    // Parse the contents of the path suppression file.
-    //
-    // The file format is as follows:
-    // * `#` introduces a comment until the end of the line.
-    // * Empty lines are ignored.
-    // * Active lines consist of a one-character command followed by data.
-    // * A line beginning with a `.` lists diagnostics to enable. These
-    //   are comma-separated and currently allow: `buffers`, `libc`.
-    // * A line beginning with '$' or '@' is silently ignored (reserved
-    //   for rolling in future features without breakage).
-    // * Every other line is a path prefix from the source tree root using
-    //   unix-style delimiters:
-    //   * Each line either removes a path from checks or adds a path to checks.
-    //   * If the line starts with `+` paths matching the line will be added.
-    //   * If the line starts with `-` paths matching the line will removed.
-    //   * Other starting characters are not allowed.
-    //   * Paths naming directories match the entire sub-directory. For instance
-    //     `+a/b/` will match the file at `//a/b/c.h` but will *not* match
-    //     `//other/a/b/c.h`.
-    //   * Paths naming files match the single file and look like `+a/b/c.h`.
-    //   * Trailing slashes for directories are recommended, but not enforced.
-    //   * The longest (most specific) match takes precedence.
-    //   * Files that do not match any of the prefixes file will be checked.
-    //   * Duplicate prefixes are not allowed and produce compilation errors.
-    //
-    // Example:
-    // ```
-    // # A file of path prefixes.
-    // # Matches anything under the directory //foo/bar, opting them into
-    // # checks.
-    // +foo/bar/
-    // # Avoids checks in the //my directory.
-    // -my/
-    // # Matches a specific file at //my/file.cc, overriding the `-my/` above
-    // # for this one file.
-    // +my/file.cc
-    // ```
-    //
-    llvm::StringRef string = check_file_prefixes_.buffer->getBuffer();
-    while (!string.empty()) {
-      auto [line, remainder] = string.split('\n');
-      string = remainder;
-      auto [active, comment] = line.split('#');
-      active = active.trim();
-      if (active.empty()) {
-        continue;
-      }
-      char symbol = active[0u];
-      if (symbol == '.') {
-        // A "dot" line contains directives to enable.
-        if (active.contains("buffers")) {
-          check_file_prefixes_.check_buffers = true;
-        }
-        if (active.contains("libc")) {
-          check_file_prefixes_.check_libc_calls = true;
-        }
-        continue;
-      }
-      if (symbol == '$') {
-        // Reserved for future expansion, path to root.
-        continue;
-      }
-      if (symbol == '@') {
-        // Reserved for future expansion, file inclusion.
-        continue;
-      }
-      if (symbol != '+' && symbol != '-') {
-        llvm::errs() << "[unsafe-buffers] Invalid line in paths file, must "
-                     << "start with +/-: '" << line << "'\n";
-        return false;
-      }
-      llvm::StringRef prefix = active.substr(1u).rtrim('/');
-      if (prefix.empty()) {
-        llvm::errs() << "[unsafe-buffers] Invalid line in paths file, path "
-                     << "must immediately follow +/-: '" << line << "'\n";
-        return false;
-      }
-      auto [ignore, was_inserted] =
-          check_file_prefixes_.prefix_map.insert({prefix, symbol});
-      if (!was_inserted) {
-        llvm::errs() << "[unsafe-buffers] Duplicate entry in paths file "
-                        "for '"
-                     << line << "'\n";
-        return false;
-      }
-    }
-    return true;
-  }
-
  private:
-  CheckFilePrefixes check_file_prefixes_;
-  bool moved_prefixes_ = false;
+  UnsafeBuffersConfig unsafe_buffers_config_;
+  bool moved_config_ = false;
 };
 
 class AllowUnsafeBuffersPragmaHandler : public clang::PragmaHandler {
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py
index 6f52362..302f8707 100755
--- a/tools/clang/scripts/build.py
+++ b/tools/clang/scripts/build.py
@@ -844,6 +844,8 @@
   if not args.skip_checkout:
     with timer.time('checkout llvm'):
       CheckoutGitRepo('LLVM monorepo', LLVM_GIT_URL, checkout_revision, LLVM_DIR)
+      # TODO(crbug.com/435127246): remove once we roll past this revision
+      GitCherryPick(LLVM_DIR, '69b8d6d4ead01b88fb8d6642914ca7492e32fdb6')
 
   if args.llvm_force_head_revision:
     CLANG_REVISION = GetCommitDescription(checkout_revision)
diff --git a/tools/clang/scripts/expand_thin_archives.py b/tools/clang/scripts/expand_thin_archives.py
deleted file mode 100755
index ed2fd69..0000000
--- a/tools/clang/scripts/expand_thin_archives.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env python3
-# Copyright 2019 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Library and tool to expand command lines that mention thin archives
-# into command lines that mention the contained object files.
-
-import argparse
-import sys
-
-from remote_link import RemoteLinkWindows
-from remote_ld import RemoteLinkUnix
-
-
-def main(argv):
-  ap = argparse.ArgumentParser(
-      description=('Expand command lines that mention thin archives into '
-                   'command lines that mention the contained object files.'),
-      usage='%(prog)s [options] -- command line')
-  ap.add_argument('-o',
-                  '--output',
-                  help=('Write new command line to named file '
-                        'instead of standard output.'))
-  ap.add_argument('-p', '--linker-prefix',
-                  help='String to prefix linker flags with.',
-                  default='')
-  ap.add_argument('cmdline',
-                  nargs=argparse.REMAINDER,
-                  help='Command line to expand. Should be preceded by \'--\'.')
-  args = ap.parse_args(argv[1:])
-  if not args.cmdline:
-    ap.print_help(sys.stderr)
-    return 1
-
-  cmdline = args.cmdline
-  if cmdline[0] == '--':
-    cmdline = cmdline[1:]
-  linker_prefix = args.linker_prefix
-
-  if linker_prefix == '-Wl,':
-    linker = RemoteLinkUnix()
-  else:
-    linker = RemoteLinkWindows()
-
-  rsp_expanded = list(linker.expand_args_rsps(cmdline))
-  expanded_args = list(linker.expand_thin_archives(rsp_expanded))
-
-  if args.output:
-    output = open(args.output, 'w')
-  else:
-    output = sys.stdout
-  for arg in expanded_args:
-    output.write('%s\n' % (arg,))
-  if args.output:
-    output.close()
-  return 0
-
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv))
diff --git a/tools/clang/scripts/remote_ld.py b/tools/clang/scripts/remote_ld.py
deleted file mode 100755
index 5207070..0000000
--- a/tools/clang/scripts/remote_ld.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#! /usr/bin/env python3
-# Copyright 2020 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Linker wrapper that performs distributed ThinLTO on Reclient.
-#
-# Usage: Pass the original link command as parameters to this script.
-# E.g. original: clang++ -o foo foo.o
-# Becomes: remote_ld clang++ -o foo foo.o
-
-import os
-import re
-import sys
-
-import remote_link
-
-
-class RemoteLinkUnix(remote_link.RemoteLinkBase):
-  # Target-platform-specific constants.
-  WL = '-Wl,'
-  TLTO = '-plugin-opt=thinlto'
-  SEP = '='
-  DATA_SECTIONS = '-fdata-sections'
-  FUNCTION_SECTIONS = '-ffunction-sections'
-  GROUP_RE = re.compile(WL + '--(?:end|start)-group')
-  MACHINE_RE = re.compile('-m([0-9]+)')
-  OBJ_PATH = '-plugin-opt=obj-path' + SEP
-  OBJ_SUFFIX = '.o'
-  PREFIX_REPLACE = TLTO + '-prefix-replace' + SEP
-  XIR = '-x ir '
-
-  ALLOWLIST = {
-      'chrome',
-  }
-
-  def analyze_args(self, args, *posargs, **kwargs):
-    # TODO(crbug.com/40113922): Builds are unreliable when all targets use
-    # distributed ThinLTO, so we only enable it for some targets.
-    # For other targets, we fall back to local ThinLTO. We must use ThinLTO
-    # because we build with -fsplit-lto-unit, which requires code generation
-    # be done for each object and target.
-    # Returning None from this function causes the original, non-distributed
-    # linker command to be invoked.
-    if args.output is None:
-      return None
-    if not (args.allowlist or os.path.basename(args.output) in self.ALLOWLIST):
-      return None
-    return super(RemoteLinkUnix, self).analyze_args(args, *posargs, **kwargs)
-
-  def process_output_param(self, args, i):
-    """
-    If args[i] is a parameter that specifies the output file,
-    returns (output_name, new_i). Else, returns (None, new_i).
-    """
-    if args[i] == '-o':
-      return (os.path.normpath(args[i + 1]), i + 2)
-    else:
-      return (None, i + 1)
-
-
-if __name__ == '__main__':
-  sys.exit(RemoteLinkUnix().main(sys.argv))
diff --git a/tools/clang/scripts/remote_link.py b/tools/clang/scripts/remote_link.py
deleted file mode 100755
index 82bfd750..0000000
--- a/tools/clang/scripts/remote_link.py
+++ /dev/null
@@ -1,689 +0,0 @@
-#! /usr/bin/env python3
-# Copyright 2020 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Linker wrapper that performs distributed ThinLTO on Reclient.
-#
-# Usage: Pass the original link command as parameters to this script.
-# E.g. original: lld-link -out:foo foo.obj
-# Becomes: remote_link.py lld-link -out:foo foo.obj
-
-import argparse
-import errno
-import io
-import os
-import re
-import shlex
-import subprocess
-import sys
-from collections import namedtuple
-from pipes import quote as shquote
-from tempfile import NamedTemporaryFile
-
-# Type returned by analyze_args.
-AnalyzeArgsResult = namedtuple('AnalyzeArgsResult', [
-    'output', 'linker', 'compiler', 'splitfile', 'index_inputs', 'index_params',
-    'codegen', 'codegen_params', 'final_inputs', 'final_params'
-])
-
-
-def autoninja():
-  """
-  Returns the name of the autoninja executable to invoke.
-  """
-  name = os.path.normpath(
-      os.path.join(os.path.dirname(__file__), '..', '..', '..', 'third_party',
-                   'depot_tools', 'autoninja'))
-  if os.name == 'nt':
-    return name + '.bat'
-  else:
-    return name
-
-
-def ensure_dir(path):
-  """
-  Creates path as a directory if it does not already exist.
-  """
-  if not path:
-    return
-  try:
-    os.makedirs(path)
-  except OSError as e:
-    if e.errno != errno.EEXIST:
-      raise
-
-
-def ensure_file(path):
-  """
-  Creates an empty file at path if it does not already exist.
-  Also creates directories as needed.
-  """
-  ensure_dir(os.path.dirname(path))
-  try:
-    fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o644)
-    os.close(fd)
-  except OSError as e:
-    if e.errno != errno.EEXIST:
-      raise
-
-
-def exe_suffix():
-  if os.name == 'nt':
-    return '.exe'
-  else:
-    return ''
-
-
-def is_bitcode_file(path):
-  """
-  Returns True if path contains a LLVM bitcode file, False if not.
-  """
-  with open(path, 'rb') as f:
-    return f.read(4) == b'BC\xc0\xde'
-
-
-def is_thin_archive(path):
-  """
-  Returns True if path refers to a thin archive (ar file), False if not.
-  """
-  with open(path, 'rb') as f:
-    return f.read(8) == b'!<thin>\n'
-
-
-def names_in_archive(path, ar_path):
-  """
-  Yields the member names in the archive file at path.
-  """
-  proc = subprocess.run([ar_path, "t", path], stdout=subprocess.PIPE)
-  for line in proc.stdout.splitlines():
-    # Using UTF-8 here gives us a fighting chance if someone decides to use
-    # non-US-ASCII characters in a file name, and backslashreplace gives us
-    # a human-readable representation of any undecodable bytes we might
-    # encounter.
-    yield line.decode('UTF-8', 'backslashreplace').rstrip()
-
-
-def ninjaenc(s):
-  """
-  Encodes string s for use in ninja files.
-  """
-  return s.replace('$', '$$')
-
-
-def ninjajoin(l):
-  """
-  Encodes list of strings l to a string encoded for use in a ninja file.
-  """
-  return ' '.join(map(ninjaenc, l))
-
-
-def parse_args(args):
-  """
-  Parses the command line and returns a structure with the results.
-  """
-  # The basic invocation is to pass in the command line that would be used
-  # for a local ThinLTO link. Optionally, this may be preceded by options
-  # that set some values for this script. If these optional options are
-  # present, they must be followed by '--'.
-  ap = argparse.ArgumentParser()
-  ap.add_argument('--generate',
-                  action='store_true',
-                  help='generate ninja file, but do not invoke it.')
-  ap.add_argument('--wrapper', help='path to remote exec wrapper.')
-  ap.add_argument('--jobs', '-j', help='maximum number of concurrent jobs.')
-  ap.add_argument('--no-wrapper',
-                  action='store_true',
-                  help='do not use remote exec wrapper.')
-  ap.add_argument('--allowlist',
-                  action='store_true',
-                  help='act as if the target is on the allow list.')
-  ap.add_argument('--ar-path', help='path to ar or llvm-ar.', required=True)
-  try:
-    splitpos = args.index('--')
-  except:
-    raise Exception("Must separate linker args from wrapper args using --")
-  parsed = ap.parse_args(args[1:splitpos])
-  rest = args[(splitpos + 1):]
-  parsed.linker = rest[0]
-  parsed.linker_args = rest[1:]
-  return parsed
-
-
-def report_run(cmd, *args, **kwargs):
-  """
-  Runs a command using subprocess.check_call, first writing the command line
-  to standard error.
-  """
-  sys.stderr.write('%s: %s\n' % (sys.argv[0], ' '.join(map(shquote, cmd))))
-  sys.stderr.flush()
-  return subprocess.check_call(cmd, *args, **kwargs)
-
-
-class RemoteLinkBase(object):
-  """
-  Base class used by RemoteLinkUnix and RemoteLinkWindows.
-  """
-  # Defaults.
-  wrapper = 'rewrapper'
-  jobs = None
-
-  # These constants should work across platforms.
-  DATA_SECTIONS_RE = re.compile('-f(no-)?data-sections|[-/]Gw(-)?',
-                                re.IGNORECASE)
-  FUNCTION_SECTIONS_RE = re.compile('-f(no-)?function-sections|[-/]Gy(-)?',
-                                    re.IGNORECASE)
-  LIB_RE = re.compile('.*\\.(?:a|r?lib)', re.IGNORECASE)
-  # LTO_RE matches flags we want to pass in the thin link step but not in the
-  # native link step.
-  # Continue to pass -flto and -fsanitize flags in the native link even though
-  # they're not normally necessary because clang needs them to build with CFI.
-  LTO_RE = re.compile('|'.join((
-      '-Wl,-plugin-opt=.*',
-      '-Wl,--lto.*',
-      '-Wl,--thin.*',
-  )))
-  MLLVM_RE = re.compile('(?:-Wl,)?([-/]mllvm)[:=,]?(.*)', re.IGNORECASE)
-  OBJ_RE = re.compile('(.*)\\.(o(?:bj)?)', re.IGNORECASE)
-
-  def _no_codegen(self, args):
-    """
-    Helper function for the case where no distributed code generation
-    is necessary. It invokes the original command, unless --generate
-    was passed, in which case it informs the user that no code generation
-    is necessary.
-    """
-    if args.generate:
-      sys.stderr.write(
-          'No code generation required; no ninja file generated.\n')
-      return 5  # Indicates no code generation required.
-    return subprocess.call([args.linker] + args.linker_args)
-
-  def transform_codegen_param(self, param):
-    return self.transform_codegen_param_common(param)
-
-  def transform_codegen_param_common(self, param):
-    """
-    If param is a parameter relevant to code generation, returns the
-    parameter in a form that is suitable to pass to clang.  For values
-    of param that are not relevant to code generation, returns None.
-    """
-    match = self.MACHINE_RE.match(param)
-    if match and match.group(1).lower() in ['x86', 'i386', 'arm', '32']:
-      return ['-m32']
-    match = self.MLLVM_RE.match(param)
-    if match:
-      if match.group(2):
-        return ['-mllvm', match.group(2)]
-      else:
-        return ['-mllvm']
-    if (param.startswith('-f') and not param.startswith('-flto')
-        and not param.startswith('-fsanitize')
-        and not param.startswith('-fthinlto')
-        and not param.startswith('-fwhole-program')):
-      return [param]
-    if param.startswith('-g'):
-      return [param]
-    if param.startswith('-m'):
-      # Note: -mllvm is handled separately above.
-      return [param]
-    if param.startswith('--target'):
-      return [param]
-    return None
-
-  def output_path(self, args):
-    """
-    Analyzes command line arguments in args and returns the output
-    path if one is specified by args. If no output path is specified
-    by args, returns None.
-    """
-    i = 2
-    while i < len(args):
-      output, next_i = self.process_output_param(args, i)
-      if output is not None:
-        return output
-      i = next_i
-    return None
-
-  def write_rsp(self, path, params):
-    """
-    Writes params to a newly created response file at path.
-    """
-    ensure_dir(os.path.basename(path))
-    with open(path, 'wb') as f:
-      f.write('\n'.join(map(self.rspenc, params)).encode('UTF-8'))
-
-  def rspenc(self, param):
-    """
-    Encodes param for use in an rsp file.
-    """
-    return param.replace('\\%', '%')
-
-  def expand_rsp(self, rspname):
-    """
-    Returns the parameters found in the response file at rspname.
-    """
-    with open(rspname) as f:
-      return shlex.split(f.read())
-
-  def expand_args_rsps(self, args):
-    """
-    Yields args, expanding @rsp file references into the commands mentioned
-    in the rsp file.
-    """
-    result = []
-    for arg in args:
-      if len(arg) > 0 and arg[0] == '@':
-        for x in self.expand_rsp(arg[1:]):
-          yield x
-      else:
-        yield arg
-
-  def expand_archives(self, args, ar_path):
-    """
-    Yields the parameters in args, with archives replaced by a sequence
-    of '--start-lib', the member names, and '--end-lib'. This is used to get a
-    command line where members of archives are mentioned explicitly, but we
-    still get the same semantics as using archive files, namely that the object
-    files are only linked in if they provided needed symbol definitions.
-    Most of the archives encountered in the Chromium link process are
-    "thin archives", which means they're just directories of files that are
-    already on disk - and all we need to do is enumerate those files instead
-    of actually extracting anything. Occasionally we encounter a real archive
-    and its contents need to be extracted.
-    """
-    for arg in args:
-      if arg.startswith("./"):
-        arg = arg[2:]
-      if self.LIB_RE.match(arg) and os.path.exists(arg):
-        yield (self.WL + '--start-lib')
-        if is_thin_archive(arg):
-          for name in names_in_archive(arg, ar_path):
-            yield (name)
-        else:
-          arg_encoded = arg.replace("..", "parent_dir")
-          extractdir = os.path.join("expanded_archives", arg_encoded)
-          if not os.path.exists(extractdir):
-            os.makedirs(extractdir)
-          subprocess.run([ar_path, "--output", extractdir, "x", arg])
-          for name in names_in_archive(arg, ar_path):
-            yield (os.path.join(extractdir, name))
-        yield (self.WL + '--end-lib')
-      else:
-        yield (arg)
-
-  def analyze_args(self, args, gen_dir, common_dir, use_common_objects,
-                   ar_path):
-    """
-    Analyzes the command line arguments in args.
-    If no ThinLTO code generation is necessary, returns None.
-    Else, returns an AnalyzeArgsResult value.
-
-    Args:
-      args: the command line as returned by parse_args().
-      gen_dir: directory in which to generate files specific to this target.
-      common_dir: directory for file shared among targets.
-      use_common_objects: if True, native object files are shared with
-        other targets.
-    """
-    # If we're invoking the NaCl toolchain, don't do distributed code
-    # generation.
-    if os.path.basename(args.linker).startswith('pnacl-'):
-      return None
-
-    rsp_expanded = list(self.expand_args_rsps(args.linker_args))
-    expanded_args = list(self.expand_archives(rsp_expanded, ar_path))
-
-    return self.analyze_expanded_args(expanded_args, args.output, args.linker,
-                                      gen_dir, common_dir, use_common_objects)
-
-  def analyze_expanded_args(self, args, output, linker, gen_dir, common_dir,
-                            use_common_objects):
-    """
-    Helper function for analyze_args. This is called by analyze_args after
-    expanding rsp files and determining which files are bitcode files, and
-    produces codegen_params, final_params, and index_params.
-
-    This function interacts with the filesystem through os.path.exists,
-    is_bitcode_file, and ensure_file.
-    """
-    if 'clang' in os.path.basename(linker):
-      compiler = linker
-    else:
-      compiler_dir = os.path.dirname(linker)
-      if compiler_dir:
-        compiler_dir += '/'
-      else:
-        compiler_dir = ''
-      compiler = compiler_dir + 'clang-cl' + exe_suffix()
-
-    if use_common_objects:
-      obj_dir = common_dir
-    else:
-      obj_dir = gen_dir
-
-    common_index = common_dir + '/empty.thinlto.bc'
-    index_inputs = set()
-    index_params = []
-    codegen = []
-    codegen_params = [
-        '-Wno-unused-command-line-argument',
-        '-Wno-override-module',
-    ]
-    final_inputs = set()
-    final_params = []
-    in_mllvm = [False]
-
-    # Defaults that match those for local linking.
-    optlevel = [2]
-    data_sections = [True]
-    function_sections = [True]
-
-    def extract_opt_level(param):
-      """
-      If param is a parameter that specifies the LTO optimization level,
-      returns the level. If not, returns None.
-      """
-      match = re.match('(?:-Wl,)?--lto-O(.+)', param)
-      if match:
-        return match.group(1)
-      match = re.match('[-/]opt:.*lldlto=([^:]*)', param, re.IGNORECASE)
-      if match:
-        return match.group(1)
-      return None
-
-    def process_param(param):
-      """
-      Common code for processing a single parameter from the either the
-      command line or an rsp file.
-      """
-
-      def helper():
-        """
-        This exists so that we can use return instead of
-        nested if statements to use the first matching case.
-        """
-        # After -mllvm, just pass on the param.
-        if in_mllvm[0]:
-          if param.startswith('-Wl,'):
-            codegen_params.append(param[4:])
-          else:
-            codegen_params.append(param)
-          in_mllvm[0] = False
-          return
-
-        # Check for params that specify LTO optimization level.
-        o = extract_opt_level(param)
-        if o is not None:
-          optlevel[0] = o
-          return
-
-        # Check for params that affect code generation.
-        cg_param = self.transform_codegen_param(param)
-        if cg_param:
-          codegen_params.extend(cg_param)
-          # No return here, we still want to check for -mllvm.
-
-        # Check for -mllvm.
-        match = self.MLLVM_RE.match(param)
-        if match and not match.group(2):
-          # Next parameter will be the thing to pass to LLVM.
-          in_mllvm[0] = True
-
-      # Parameters that override defaults disable the defaults; the
-      # final value is set by passing through the parameter.
-      if self.DATA_SECTIONS_RE.match(param):
-        data_sections[0] = False
-      if self.FUNCTION_SECTIONS_RE.match(param):
-        function_sections[0] = False
-
-      helper()
-      if self.GROUP_RE.match(param):
-        return
-      index_params.append(param)
-      if os.path.exists(param):
-        index_inputs.add(param)
-        match = self.OBJ_RE.match(param)
-        if match and is_bitcode_file(param):
-          native = obj_dir + '/' + match.group(1) + '.' + match.group(2)
-          if use_common_objects:
-            index = common_index
-          else:
-            index = obj_dir + '/' + param + '.thinlto.bc'
-            ensure_file(index)
-          codegen.append((os.path.normpath(native), param, index))
-        else:
-          final_inputs.add(param)
-          final_params.append(param)
-      elif not self.LTO_RE.match(param):
-        final_params.append(param)
-
-    index_params.append(self.WL + self.PREFIX_REPLACE + ';' + obj_dir + '/')
-    i = 0
-    while i < len(args):
-      x = args[i]
-      if not self.GROUP_RE.match(x):
-        outfile, next_i = self.process_output_param(args, i)
-        if outfile is not None:
-          index_params.extend(args[i:next_i])
-          final_params.extend(args[i:next_i])
-          i = next_i - 1
-        else:
-          process_param(x)
-      i += 1
-
-    # If we are not doing ThinLTO codegen, just invoke the original command.
-    if len(codegen) < 1:
-      return None
-
-    codegen_params.append('-O' + str(optlevel[0]))
-    if data_sections[0]:
-      codegen_params.append(self.DATA_SECTIONS)
-    if function_sections[0]:
-      codegen_params.append(self.FUNCTION_SECTIONS)
-
-    if use_common_objects:
-      splitfile = None
-      for tup in codegen:
-        final_params.append(tup[0])
-      index_inputs = []
-    else:
-      splitfile = gen_dir + '/' + output + '.split' + self.OBJ_SUFFIX
-      final_params.append(splitfile)
-      index_params.append(self.WL + self.OBJ_PATH + splitfile)
-      used_obj_file = gen_dir + '/' + os.path.basename(output) + '.objs'
-      final_params.append('@' + used_obj_file)
-
-    return AnalyzeArgsResult(
-        output=output,
-        linker=linker,
-        compiler=compiler,
-        splitfile=splitfile,
-        index_inputs=index_inputs,
-        index_params=index_params,
-        codegen=codegen,
-        codegen_params=codegen_params,
-        final_inputs=final_inputs,
-        final_params=final_params,
-    )
-
-  def gen_ninja(self, ninjaname, params, gen_dir):
-    """
-    Generates a ninja build file at path ninjaname, using original command line
-    params and with objs being a list of bitcode files for which to generate
-    native code.
-    """
-    if self.wrapper:
-      wrapper_prefix = ninjaenc(self.wrapper) + ' '
-    else:
-      wrapper_prefix = ''
-    base = gen_dir + '/' + os.path.basename(params.output)
-    ensure_dir(gen_dir)
-    ensure_dir(os.path.dirname(ninjaname))
-    codegen_cmd = ('%s%s -c %s -fthinlto-index=$index %s$bitcode -o $native' %
-                   (wrapper_prefix, ninjaenc(params.compiler),
-                    ninjajoin(params.codegen_params), self.XIR))
-    if params.index_inputs:
-      used_obj_file = base + '.objs'
-      index_rsp = base + '.index.rsp'
-      ensure_dir(os.path.dirname(used_obj_file))
-      if params.splitfile:
-        ensure_dir(os.path.dirname(params.splitfile))
-        # We use grep here to only codegen native objects which are actually
-        # used by the native link step. Ninja 1.10 introduced a dyndep feature
-        # which allows for a more elegant implementation, but Chromium still
-        # uses an older ninja version which doesn't have this feature.
-        codegen_cmd = '( ! grep -qF $native %s || %s)' % (
-            ninjaenc(used_obj_file), codegen_cmd)
-
-    with open(ninjaname, 'w') as f:
-      if params.index_inputs:
-        self.write_rsp(index_rsp, params.index_params)
-        f.write('\nrule index\n  command = %s %s %s @$rspfile\n' %
-                (ninjaenc(params.linker),
-                 ninjaenc(self.WL + self.TLTO + '-index-only' + self.SEP) +
-                 '$out', self.WL + self.TLTO + '-emit-imports-files'))
-
-      f.write(('\nrule native-link\n  command = %s @$rspname'
-               '\n  rspfile = $rspname\n  rspfile_content = $params\n') %
-              (ninjaenc(params.linker), ))
-
-      f.write('\nrule codegen\n  command = %s && touch $out\n' %
-              (codegen_cmd, ))
-
-      native_link_deps = []
-      if params.index_inputs:
-        f.write(
-            ('\nbuild %s | %s : index %s\n'
-             '  rspfile = %s\n'
-             '  rspfile_content = %s\n') %
-            (ninjaenc(used_obj_file), ninjajoin(
-                [x[2] for x in params.codegen]), ninjajoin(params.index_inputs),
-             ninjaenc(index_rsp), ninjajoin(params.index_params)))
-        native_link_deps.append(used_obj_file)
-
-      for tup in params.codegen:
-        obj, bitcode, index = tup
-        stamp = obj + '.stamp'
-        native_link_deps.append(obj)
-        f.write(
-            ('\nbuild %s : codegen %s %s\n'
-             '  bitcode = %s\n'
-             '  index = %s\n'
-             '  native = %s\n'
-             '\nbuild %s : phony %s\n') % tuple(
-                 map(ninjaenc,
-                     (stamp, bitcode, index, bitcode, index, obj, obj, stamp))))
-
-      f.write(('\nbuild %s : native-link %s\n'
-               '  rspname = %s\n  params = %s\n') %
-              (ninjaenc(params.output),
-               ninjajoin(list(params.final_inputs) + native_link_deps),
-               ninjaenc(base + '.final.rsp'), ninjajoin(params.final_params)))
-
-      f.write('\ndefault %s\n' % (ninjaenc(params.output), ))
-
-  def do_main(self, argv):
-    """
-    This function contains the main code to run. Not intended to be called
-    directly. Call main instead, which returns exit status for failing
-    subprocesses.
-    """
-    args = parse_args(argv)
-    args.output = self.output_path(argv[1:])
-    if args.output is None:
-      return self._no_codegen(args)
-    if args.wrapper:
-      self.wrapper = args.wrapper
-    if args.no_wrapper:
-      self.wrapper = None
-    if args.jobs:
-      self.jobs = int(args.jobs)
-
-    basename = os.path.basename(args.output)
-    # Only generate tailored native object files for targets on the allow list.
-    # TODO: Find a better way to structure this. There are three different
-    # ways we can perform linking: Local ThinLTO, distributed ThinLTO,
-    # and distributed ThinLTO with common object files.
-    # We expect the distributed ThinLTO variants to be faster, but
-    # common object files cannot be used when -fsplit-lto-unit is in effect.
-    # Currently, we don't detect this situation. We could, but it might
-    # be better to instead move this logic out of this script and into
-    # the build system.
-    use_common_objects = not (args.allowlist or basename in self.ALLOWLIST)
-    common_dir = 'common_objs'
-    gen_dir = 'lto.' + basename
-    params = self.analyze_args(args, gen_dir, common_dir, use_common_objects,
-                               args.ar_path)
-    # If we determined that no distributed code generation need be done, just
-    # invoke the original command.
-    if params is None:
-      return self._no_codegen(args)
-    if use_common_objects:
-      objs = [x[0] for x in params.codegen]
-      ensure_file(common_dir + '/empty.thinlto.bc')
-    ninjaname = gen_dir + '/build.ninja'
-    self.gen_ninja(ninjaname, params, gen_dir)
-    if args.generate:
-      sys.stderr.write('Generated ninja file %s\n' % (shquote(ninjaname), ))
-    else:
-      cmd = [autoninja(), '-f', ninjaname]
-      if self.jobs:
-        cmd.extend(['-j', str(self.jobs)])
-      report_run(cmd)
-    return 0
-
-  def main(self, argv):
-    try:
-      return self.do_main(argv)
-    except subprocess.CalledProcessError as e:
-      return e.returncode
-
-
-class RemoteLinkWindows(RemoteLinkBase):
-  # Target-platform-specific constants.
-  WL = ''
-  TLTO = '-thinlto'
-  SEP = ':'
-  DATA_SECTIONS = '-Gw'
-  FUNCTION_SECTIONS = '-Gy'
-  GROUP_RE = re.compile(WL + '--(?:end|start)-group')
-  MACHINE_RE = re.compile('[-/]machine:(.*)', re.IGNORECASE)
-  OBJ_PATH = '-lto-obj-path' + SEP
-  OBJ_SUFFIX = '.obj'
-  OUTPUT_RE = re.compile('[-/]out:(.*)', re.IGNORECASE)
-  PREFIX_REPLACE = TLTO + '-prefix-replace' + SEP
-  XIR = ''
-
-  ALLOWLIST = {
-      'chrome.exe',
-      'chrome.dll',
-      'chrome_child.dll',
-      # TODO: The following targets are on the allow list because the
-      # common objects flow does not link them successfully. This should
-      # be fixed, after which they can be removed from the list.
-      'tls_edit.exe',
-  }
-
-  def transform_codegen_param(self, param):
-    # In addition to parameters handled by transform_codegen_param_common,
-    # we pass on parameters that start in 'G' or 'Q', which are
-    # MSVC-style parameters that affect code generation.
-    if len(param) >= 2 and param[0] in ['-', '/'] and param[1] in ['G', 'Q']:
-      return [param]
-    return self.transform_codegen_param_common(param)
-
-  def process_output_param(self, args, i):
-    """
-    If args[i] is a parameter that specifies the output file,
-    returns (output_name, new_i). Else, returns (None, new_i).
-    """
-    m = self.OUTPUT_RE.match(args[i])
-    if m:
-      return (os.path.normpath(m.group(1)), i + 1)
-    else:
-      return (None, i + 1)
-
-
-if __name__ == '__main__':
-  sys.exit(RemoteLinkWindows().main(sys.argv))
diff --git a/tools/clang/scripts/remote_link_integration_tests.py b/tools/clang/scripts/remote_link_integration_tests.py
deleted file mode 100755
index 01a2b530..0000000
--- a/tools/clang/scripts/remote_link_integration_tests.py
+++ /dev/null
@@ -1,626 +0,0 @@
-#! /usr/bin/env python3
-# Copyright 2020 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Integration tests for remote_link.
-#
-# Usage:
-#
-# Ensure that rewrapper, llvm-objdump, and llvm-dwarfdump are in your
-# PATH.
-# Then run:
-#
-#   tools/clang/scripts/remote_link_integration_tests.py
-#
-# See also remote_link_unit_tests.py, which contains unit tests and
-# instructions for generating coverage information.
-
-import remote_ld
-import remote_link
-
-from io import StringIO
-import os
-import re
-import shlex
-import subprocess
-import unittest
-from unittest import mock
-
-from remote_link_test_utils import named_directory, working_directory
-
-# Path constants.
-CHROMIUM_DIR = os.path.abspath(
-    os.path.join(os.path.dirname(__file__), '..', '..', '..'))
-LLVM_BIN_DIR = os.path.join(CHROMIUM_DIR, 'third_party', 'llvm-build',
-                            'Release+Asserts', 'bin')
-
-
-def _create_inputs(path):
-  """
-  Creates input files under path.
-  """
-  with open(os.path.join(path, 'main.cpp'), 'w') as f:
-    f.write('extern int foo();\n'
-            'int main(int argc, char *argv[]) {\n  return foo();\n}\n')
-  with open(os.path.join(path, 'foo.cpp'), 'w') as f:
-    f.write('int foo() {\n  return 12;\n}\n')
-  with open(os.path.join(path, 'bar.cpp'), 'w') as f:
-    f.write('int bar() {\n  return 9;\n}\n')
-
-
-def _lto_args(generate_bitcode):
-  """
-  Returns list of arguments to clang to generate bitcode or not.
-  """
-  if generate_bitcode:
-    return ['-flto=thin']
-  else:
-    return []
-
-
-class RemoteLinkUnixAllowMain(remote_ld.RemoteLinkUnix):
-  """
-  Same as remote_ld.RemoteLinkUnix, but has "main" on the allow list.
-  """
-
-  def __init__(self, *args, **kwargs):
-    super(RemoteLinkUnixAllowMain, self).__init__(*args, **kwargs)
-    self.ALLOWLIST = {'main'}
-
-
-class RemoteLinkWindowsAllowMain(remote_link.RemoteLinkWindows):
-  """
-  Same as remote_ld.RemoteLinkWindows, but has "main" on the allow list.
-  """
-
-  def __init__(self, *args, **kwargs):
-    super(RemoteLinkWindowsAllowMain, self).__init__(*args, **kwargs)
-    self.ALLOWLIST = {'main.exe'}
-
-
-class RemoteLinkIntegrationTest(unittest.TestCase):
-  def clangcl(self):
-    return os.path.join(LLVM_BIN_DIR, 'clang-cl' + remote_link.exe_suffix())
-
-  def lld_link(self):
-    return os.path.join(LLVM_BIN_DIR, 'lld-link' + remote_link.exe_suffix())
-
-  def llvmar(self):
-    return os.path.join(LLVM_BIN_DIR, 'llvm-ar' + remote_link.exe_suffix())
-
-  def test_distributed_lto_common_objs(self):
-    with named_directory() as d, working_directory(d):
-      _create_inputs(d)
-      os.makedirs('obj')
-      subprocess.check_call([
-          self.clangcl(), '-c', '-Os', '-flto=thin', 'main.cpp',
-          '-Foobj/main.obj'
-      ])
-      subprocess.check_call([
-          self.clangcl(), '-c', '-Os', '-flto=thin', 'foo.cpp', '-Foobj/foo.obj'
-      ])
-      subprocess.check_call([
-          self.clangcl(), '-c', '-Os', '-flto=thin', 'bar.cpp', '-Foobj/bar.obj'
-      ])
-      subprocess.check_call([
-          self.llvmar(), 'crsT', 'obj/foobar.lib', 'obj/bar.obj', 'obj/foo.obj'
-      ])
-      with open('main.rsp', 'w') as f:
-        f.write('obj/main.obj\n' 'obj/foobar.lib\n')
-      with open('my_reclient.sh', 'w') as f:
-        f.write('#! /bin/sh\n\nrewrapper "$@"\n')
-      os.chmod('my_reclient.sh', 0o755)
-      rc = remote_link.RemoteLinkWindows().main([
-          'remote_link.py', '--wrapper', './my_reclient.sh', '--ar-path',
-          self.llvmar(), '--',
-          self.lld_link(), '-nodefaultlib', '-entry:main', '-out:main.exe',
-          '@main.rsp'
-      ])
-      # Should succeed.
-      self.assertEqual(rc, 0)
-      # Check codegen parameters.
-      with open(os.path.join(d, 'lto.main.exe', 'build.ninja')) as f:
-        buildrules = f.read()
-        codegen_match = re.search('^rule codegen\\b.*?^[^ ]', buildrules,
-                                  re.MULTILINE | re.DOTALL)
-        self.assertIsNotNone(codegen_match)
-        codegen_text = codegen_match.group(0)
-        self.assertIn('my_reclient.sh', codegen_text)
-        self.assertNotIn('-flto', codegen_text)
-        self.assertIn('build common_objs/obj/main.obj.stamp : codegen ',
-                      buildrules)
-        self.assertIn('build common_objs/obj/foo.obj.stamp : codegen ',
-                      buildrules)
-        self.assertIn(' index = common_objs/empty.thinlto.bc', buildrules)
-        link_match = re.search('^build main.exe : native-link\\b.*?^[^ ]',
-                               buildrules, re.MULTILINE | re.DOTALL)
-        self.assertIsNotNone(link_match)
-        link_text = link_match.group(0)
-        self.assertNotIn('main.exe.split.obj', link_text)
-      # Check that main does not call foo.
-      disasm = subprocess.check_output(['llvm-objdump', '-d', 'main.exe'])
-      # There are no symbols in the disassembly, but we're expecting two
-      # functions, one of which calls the other.
-      self.assertTrue(b'call' in disasm or b'jmp' in disasm)
-
-  def test_distributed_lto_allowlist(self):
-    with named_directory() as d, working_directory(d):
-      _create_inputs(d)
-      os.makedirs('obj')
-      subprocess.check_call([
-          self.clangcl(), '-c', '-Os', '-flto=thin', '-m32', 'main.cpp',
-          '-Foobj/main.obj'
-      ])
-      subprocess.check_call([
-          self.clangcl(), '-c', '-Os', '-flto=thin', '-m32', 'foo.cpp',
-          '-Foobj/foo.obj'
-      ])
-      subprocess.check_call([
-          self.clangcl(), '-c', '-Os', '-flto=thin', '-m32', 'bar.cpp',
-          '-Foobj/bar.obj'
-      ])
-      subprocess.check_call([
-          self.llvmar(), 'crsT', 'obj/foobar.lib', 'obj/bar.obj', 'obj/foo.obj'
-      ])
-      with open('main.rsp', 'w') as f:
-        f.write('obj/main.obj\n' 'obj/foobar.lib\n')
-      rc = RemoteLinkWindowsAllowMain().main([
-          'remote_link.py', '--wrapper', 'rewrapper', '--ar-path',
-          self.llvmar(), '--',
-          self.lld_link(), '-nodefaultlib', '-entry:main', '-machine:X86',
-          '-opt:lldlto=2', '-mllvm:-import-instr-limit=10', '-out:main.exe',
-          '@main.rsp'
-      ])
-      # Should succeed.
-      self.assertEqual(rc, 0)
-      # Check codegen parameters.
-      with open(os.path.join(d, 'lto.main.exe', 'build.ninja')) as f:
-        buildrules = f.read()
-        codegen_match = re.search('^rule codegen\\b.*?^[^ ]', buildrules,
-                                  re.MULTILINE | re.DOTALL)
-        self.assertIsNotNone(codegen_match)
-        codegen_text = codegen_match.group(0)
-        self.assertIn('rewrapper', codegen_text)
-        self.assertIn('-m32', codegen_text)
-        self.assertIn('-mllvm -import-instr-limit=10', codegen_text)
-        self.assertNotIn('-flto', codegen_text)
-        self.assertIn('build lto.main.exe/obj/main.obj.stamp : codegen ',
-                      buildrules)
-        self.assertIn('build lto.main.exe/obj/foo.obj.stamp : codegen ',
-                      buildrules)
-        link_match = re.search('^build main.exe : native-link\\b.*?^[^ ]',
-                               buildrules, re.MULTILINE | re.DOTALL)
-        self.assertIsNotNone(link_match)
-        link_text = link_match.group(0)
-        self.assertIn('main.exe.split.obj', link_text)
-      # Check that main does not call foo.
-      disasm = subprocess.check_output(['llvm-objdump', '-d', 'main.exe'])
-      # There are no symbols in the disassembly, but we're expecting a single
-      # function, with no calls or jmps.
-      self.assertNotIn(b'jmp', disasm)
-      self.assertNotIn(b'call', disasm)
-
-  def test_override_allowlist(self):
-    with named_directory() as d, working_directory(d):
-      _create_inputs(d)
-      os.makedirs('obj')
-      subprocess.check_call([
-          self.clangcl(), '-c', '-O2', '-flto=thin', 'main.cpp',
-          '-Foobj/main.obj'
-      ])
-      subprocess.check_call([
-          self.clangcl(), '-c', '-O2', '-flto=thin', 'foo.cpp', '-Foobj/foo.obj'
-      ])
-      rc = remote_link.RemoteLinkWindows().main([
-          'remote_link.py', '--generate', '--allowlist', '--ar-path',
-          self.llvmar(), '--',
-          self.lld_link(), '-nodefaultlib', '-entry:main', '-opt:lldlto=2',
-          '-out:main.exe', 'obj/main.obj', 'obj/foo.obj'
-      ])
-      # Should succeed.
-      self.assertEqual(rc, 0)
-      # Check that we have rules for main and foo, and that they are
-      # not common objects.
-      with open(os.path.join(d, 'lto.main.exe', 'build.ninja')) as f:
-        buildrules = f.read()
-        codegen_match = re.search(r'^rule codegen\b.*?^[^ ]', buildrules,
-                                  re.MULTILINE | re.DOTALL)
-        self.assertIsNotNone(codegen_match)
-        codegen_text = codegen_match.group(0)
-        self.assertNotIn('-flto', codegen_text)
-        self.assertIn('build lto.main.exe/obj/main.obj.stamp : codegen ',
-                      buildrules)
-        self.assertIn('build lto.main.exe/obj/foo.obj.stamp : codegen ',
-                      buildrules)
-        link_match = re.search(r'^build main.exe : native-link\b.*?^[^ ]',
-                               buildrules, re.MULTILINE | re.DOTALL)
-        self.assertIsNotNone(link_match)
-
-
-class RemoteLdIntegrationTest(unittest.TestCase):
-  def clangxx(self):
-    return os.path.join(LLVM_BIN_DIR, 'clang++' + remote_link.exe_suffix())
-
-  def llvmar(self):
-    return os.path.join(LLVM_BIN_DIR, 'llvm-ar' + remote_link.exe_suffix())
-
-  def test_nonlto(self):
-    with named_directory() as d, working_directory(d):
-      _create_inputs(d)
-      subprocess.check_call(
-          [self.clangxx(), '-c', '-Os', 'main.cpp', '-o', 'main.o'])
-      subprocess.check_call(
-          [self.clangxx(), '-c', '-Os', 'foo.cpp', '-o', 'foo.o'])
-      rc = RemoteLinkUnixAllowMain().main([
-          'remote_ld.py', '--wrapper', 'rewrapper', '--ar-path',
-          self.llvmar(), '--',
-          self.clangxx(), '-fuse-ld=lld', 'main.o', 'foo.o', '-o', 'main'
-      ])
-      # Should succeed.
-      self.assertEqual(rc, 0)
-      # lto.main directory should not be present.
-      self.assertFalse(os.path.exists(os.path.join(d, 'lto.main')))
-      # Check that main calls foo.
-      disasm = subprocess.check_output(['llvm-objdump', '-d', 'main'])
-      main_idx = disasm.index(b' <main>:\n')
-      after_main_idx = disasm.index(b'\n\n', main_idx)
-      main_disasm = disasm[main_idx:after_main_idx]
-      self.assertIn(b'foo', main_disasm)
-
-  def test_fallback_lto(self):
-    with named_directory() as d, working_directory(d):
-      _create_inputs(d)
-      subprocess.check_call([
-          self.clangxx(), '-c', '-Os', '-flto=thin', 'main.cpp', '-o', 'main.o'
-      ])
-      subprocess.check_call(
-          [self.clangxx(), '-c', '-Os', '-flto=thin', 'foo.cpp', '-o', 'foo.o'])
-      rc = remote_ld.RemoteLinkUnix().main([
-          'remote_ld.py', '--wrapper', 'rewrapper', '--ar-path',
-          self.llvmar(), '--',
-          self.clangxx(), '-fuse-ld=lld', '-flto=thin', 'main.o', 'foo.o', '-o',
-          'main'
-      ])
-      # Should succeed.
-      self.assertEqual(rc, 0)
-      # lto.main directory should not be present.
-      self.assertFalse(os.path.exists(os.path.join(d, 'lto.main')))
-      # Check that main does not call foo.
-      disasm = subprocess.check_output(['llvm-objdump', '-d', 'main'])
-      main_idx = disasm.index(b' <main>:\n')
-      after_main_idx = disasm.index(b'\n\n', main_idx)
-      main_disasm = disasm[main_idx:after_main_idx]
-      self.assertNotIn(b'foo', main_disasm)
-
-  def test_distributed_lto(self):
-    with named_directory() as d, working_directory(d):
-      _create_inputs(d)
-      subprocess.check_call([
-          self.clangxx(), '-c', '-Os', '-flto=thin', 'main.cpp', '-o', 'main.o'
-      ])
-      subprocess.check_call(
-          [self.clangxx(), '-c', '-Os', '-flto=thin', 'foo.cpp', '-o', 'foo.o'])
-      rc = RemoteLinkUnixAllowMain().main([
-          'remote_ld.py', '-j', '16', '--ar-path',
-          self.llvmar(), '--',
-          self.clangxx(), '-fuse-ld=lld', '-flto=thin', 'main.o', 'foo.o', '-o',
-          'main'
-      ])
-      # Should succeed.
-      self.assertEqual(rc, 0)
-      # build.ninja file should have rewrapper invocations in it.
-      with open(os.path.join(d, 'lto.main', 'build.ninja')) as f:
-        buildrules = f.read()
-        self.assertIn('rewrapper ', buildrules)
-        self.assertIn('build lto.main/main.o.stamp : codegen ', buildrules)
-        self.assertIn('build lto.main/foo.o.stamp : codegen ', buildrules)
-      # Check that main does not call foo.
-      disasm = subprocess.check_output(['llvm-objdump', '-d', 'main'])
-      main_idx = disasm.index(b' <main>:\n')
-      after_main_idx = disasm.index(b'\n\n', main_idx)
-      main_disasm = disasm[main_idx:after_main_idx]
-      self.assertNotIn(b'foo', main_disasm)
-
-  def test_distributed_lto_thin_archive_same_dir(self):
-    with named_directory() as d, working_directory(d):
-      _create_inputs(d)
-      subprocess.check_call([
-          self.clangxx(), '-c', '-Os', '-flto=thin', 'main.cpp', '-o', 'main.o'
-      ])
-      subprocess.check_call(
-          [self.clangxx(), '-c', '-Os', '-flto=thin', 'foo.cpp', '-o', 'foo.o'])
-      subprocess.check_call(
-          [self.clangxx(), '-c', '-Os', '-flto=thin', 'bar.cpp', '-o', 'bar.o'])
-      subprocess.check_call(
-          [self.llvmar(), 'crsT', 'libfoobar.a', 'bar.o', 'foo.o'])
-      rc = RemoteLinkUnixAllowMain().main([
-          'remote_ld.py', '--ar-path',
-          self.llvmar(), '--',
-          self.clangxx(), '-fuse-ld=lld', '-flto=thin', 'main.o', 'libfoobar.a',
-          '-o', 'main'
-      ])
-      # Should succeed.
-      self.assertEqual(rc, 0)
-      # build.ninja file should have rewrapper invocations in it.
-      with open(os.path.join(d, 'lto.main', 'build.ninja')) as f:
-        buildrules = f.read()
-        self.assertIn('rewrapper ', buildrules)
-        self.assertIn('build lto.main/main.o.stamp : codegen ', buildrules)
-        self.assertIn('build lto.main/foo.o.stamp : codegen ', buildrules)
-      # Check that main does not call foo.
-      disasm = subprocess.check_output(['llvm-objdump', '-d', 'main'])
-      main_idx = disasm.index(b' <main>:\n')
-      after_main_idx = disasm.index(b'\n\n', main_idx)
-      main_disasm = disasm[main_idx:after_main_idx]
-      self.assertNotIn(b'foo', main_disasm)
-
-  def test_distributed_lto_thin_archive_subdir(self):
-    self.run_archive_test(bitcode_archive=True,
-                          bitcode_main=True,
-                          thin_archive=True)
-
-  def test_distributed_machine_code_thin_archive_bitcode_main_subdir(self):
-    self.run_archive_test(bitcode_archive=False,
-                          bitcode_main=True,
-                          thin_archive=True)
-
-  def test_distributed_machine_code_thin_archive_subdir(self):
-    self.run_archive_test(bitcode_archive=False,
-                          bitcode_main=False,
-                          thin_archive=True)
-
-  def test_distributed_bitcode_thick_archive_subdir(self):
-    self.run_archive_test(bitcode_archive=True,
-                          bitcode_main=True,
-                          thin_archive=False)
-
-  def test_distributed_machine_code_thick_archive_subdir(self):
-    self.run_archive_test(bitcode_archive=False,
-                          bitcode_main=False,
-                          thin_archive=False)
-
-  def test_distributed_machine_code_thick_archive_bitcode_main_subdir(self):
-    self.run_archive_test(bitcode_archive=False,
-                          bitcode_main=True,
-                          thin_archive=False)
-
-  def run_archive_test(self, bitcode_archive, bitcode_main, thin_archive):
-    """
-    Runs a test to ensure correct remote linking handling of
-    an archive.
-    Arguments:
-    bitcode_archive: whether the archive should contain bitcode (true)
-    or machine code (false)
-    bitcode_main: whether the main object, outside the archive, should
-    contain bitcode (true) or machine code (false)
-    thin_archive: whether to create a thin archive instead of a regular archive.
-    """
-    with named_directory() as d, working_directory(d):
-      _create_inputs(d)
-      os.makedirs('obj')
-      subprocess.check_call(
-          [self.clangxx(), '-c', '-Os', 'main.cpp', '-o', 'obj/main.o'] +
-          _lto_args(bitcode_main))
-      subprocess.check_call(
-          [self.clangxx(), '-c', '-Os', 'foo.cpp', '-o', 'obj/foo.o'] +
-          _lto_args(bitcode_archive))
-      subprocess.check_call(
-          [self.clangxx(), '-c', '-Os', 'bar.cpp', '-o', 'obj/bar.o'] +
-          _lto_args(bitcode_archive))
-      archive_creation_arg = 'crs'
-      if thin_archive:
-        archive_creation_arg = 'crsT'
-      subprocess.check_call([
-          self.llvmar(), archive_creation_arg, 'obj/libfoobar.a', 'obj/bar.o',
-          'obj/foo.o'
-      ])
-      rc = RemoteLinkUnixAllowMain().main([
-          'remote_ld.py', '--ar-path',
-          self.llvmar(), '--',
-          self.clangxx(), '-fuse-ld=lld', '-flto=thin', 'obj/main.o',
-          'obj/libfoobar.a', '-o', 'main'
-      ])
-      # Should succeed.
-      self.assertEqual(rc, 0)
-      if bitcode_main or bitcode_archive:
-        # build.ninja file should have rewrapper invocations in it.
-        with open(os.path.join(d, 'lto.main', 'build.ninja')) as f:
-          buildrules = f.read()
-          self.assertIn('rewrapper ', buildrules)
-          if bitcode_main:
-            self.assertIn('build lto.main/obj/main.o.stamp : codegen ',
-                          buildrules)
-          if bitcode_archive:
-            if thin_archive:
-              self.assertIn('build lto.main/obj/foo.o.stamp : codegen ',
-                            buildrules)
-            else:
-              self.assertIn(
-                  'build lto.main/expanded_archives/obj/libfoobar.a/' +
-                  'foo.o.stamp : codegen ', buildrules)
-      # Check that main does not call foo.
-      if bitcode_archive:
-        disasm = subprocess.check_output(['llvm-objdump', '-d', 'main'])
-        main_idx = disasm.index(b' <main>:\n')
-        after_main_idx = disasm.index(b'\n\n', main_idx)
-        main_disasm = disasm[main_idx:after_main_idx]
-        self.assertNotIn(b'foo', main_disasm)
-
-  def test_debug_params(self):
-    with named_directory() as d, working_directory(d):
-      _create_inputs(d)
-      os.makedirs('obj')
-      subprocess.check_call([
-          self.clangxx(), '-c', '-g', '-gsplit-dwarf', '-flto=thin', 'main.cpp',
-          '-o', 'obj/main.o'
-      ])
-      subprocess.check_call([
-          self.clangxx(), '-c', '-g', '-gsplit-dwarf', '-flto=thin', 'foo.cpp',
-          '-o', 'obj/foo.o'
-      ])
-      with open('main.rsp', 'w') as f:
-        f.write('obj/main.o\n' 'obj/foo.o\n')
-      rc = RemoteLinkUnixAllowMain().main([
-          'remote_ld.py', '--ar-path',
-          self.llvmar(), '--',
-          self.clangxx(), '-fuse-ld=lld', '-flto=thin', '-g', '-gsplit-dwarf',
-          '-Wl,--lto-O2', '-o', 'main', '@main.rsp'
-      ])
-      # Should succeed.
-      self.assertEqual(rc, 0)
-      # Check debug info present, refers to .dwo file, and does not
-      # contain full debug info for foo.cpp.
-      dbginfo = subprocess.check_output(
-          ['llvm-dwarfdump', '-debug-info',
-           'main']).decode('utf-8', 'backslashreplace')
-      self.assertRegexpMatches(dbginfo, '\\bDW_AT_GNU_dwo_name\\b.*\\.dwo"')
-      self.assertNotRegexpMatches(dbginfo, '\\bDW_AT_name\\b.*foo\\.cpp"')
-
-  def test_distributed_lto_params(self):
-    with named_directory() as d, working_directory(d):
-      _create_inputs(d)
-      os.makedirs('obj')
-      subprocess.check_call([
-          self.clangxx(), '-c', '-Os', '-flto=thin', '-m32', '-fsplit-lto-unit',
-          '-fwhole-program-vtables', 'main.cpp', '-o', 'obj/main.o'
-      ])
-      subprocess.check_call([
-          self.clangxx(), '-c', '-Os', '-flto=thin', '-m32', '-fsplit-lto-unit',
-          '-fwhole-program-vtables', 'foo.cpp', '-o', 'obj/foo.o'
-      ])
-      subprocess.check_call([
-          self.clangxx(), '-c', '-Os', '-flto=thin', '-m32', '-fsplit-lto-unit',
-          '-fwhole-program-vtables', 'bar.cpp', '-o', 'obj/bar.o'
-      ])
-      subprocess.check_call(
-          [self.llvmar(), 'crsT', 'obj/libfoobar.a', 'obj/bar.o', 'obj/foo.o'])
-      with open('main.rsp', 'w') as f:
-        f.write('-fsplit-lto-unit\n'
-                '-fwhole-program-vtables\n'
-                'obj/main.o\n'
-                'obj/libfoobar.a\n')
-      rc = RemoteLinkUnixAllowMain().main([
-          'remote_ld.py', '--ar-path',
-          self.llvmar(), '--',
-          self.clangxx(), '-fuse-ld=lld', '-flto=thin', '-m32', '-Wl,-mllvm',
-          '-Wl,-generate-type-units', '-Wl,--lto-O2', '-o', 'main',
-          '-Wl,--start-group', '@main.rsp', '-Wl,--end-group'
-      ])
-      # Should succeed.
-      self.assertEqual(rc, 0)
-      # Check codegen parameters.
-      with open(os.path.join(d, 'lto.main', 'build.ninja')) as f:
-        buildrules = f.read()
-        codegen_match = re.search('^rule codegen\\b.*?^[^ ]', buildrules,
-                                  re.MULTILINE | re.DOTALL)
-        self.assertIsNotNone(codegen_match)
-        codegen_text = codegen_match.group(0)
-        self.assertIn('rewrapper', codegen_text)
-        self.assertIn('-m32', codegen_text)
-        self.assertIn('-mllvm -generate-type-units', codegen_text)
-        self.assertNotIn('-flto', codegen_text)
-        self.assertIn('build lto.main/obj/main.o.stamp : codegen ', buildrules)
-        self.assertIn('build lto.main/obj/foo.o.stamp : codegen ', buildrules)
-        link_match = re.search('^build main : native-link\\b.*?^[^ ]',
-                               buildrules, re.MULTILINE | re.DOTALL)
-        self.assertIsNotNone(link_match)
-        link_text = link_match.group(0)
-        self.assertIn('main.split.o', link_text)
-      # Check that main does not call foo.
-      disasm = subprocess.check_output(['llvm-objdump', '-d', 'main'])
-      main_idx = disasm.index(b' <main>:\n')
-      after_main_idx = disasm.index(b'\n\n', main_idx)
-      main_disasm = disasm[main_idx:after_main_idx]
-      self.assertNotIn(b'foo', main_disasm)
-
-  def test_no_rewrapper(self):
-    with named_directory() as d, working_directory(d):
-      _create_inputs(d)
-      subprocess.check_call([
-          self.clangxx(), '-c', '-Os', '-flto=thin', 'main.cpp', '-o', 'main.o'
-      ])
-      subprocess.check_call(
-          [self.clangxx(), '-c', '-Os', '-flto=thin', 'foo.cpp', '-o', 'foo.o'])
-      rc = RemoteLinkUnixAllowMain().main([
-          'remote_ld.py', '--ar-path',
-          self.llvmar(), '--no-wrapper', '-j', '16', '--',
-          self.clangxx(), '-fuse-ld=lld', '-flto=thin', 'main.o', 'foo.o', '-o',
-          'main'
-      ])
-      # Should succeed.
-      self.assertEqual(rc, 0)
-      # build.ninja file should not have rewrapper invocations in it.
-      with open(os.path.join(d, 'lto.main', 'build.ninja')) as f:
-        buildrules = f.read()
-        self.assertNotIn('rewrapper ', buildrules)
-        self.assertIn('build lto.main/main.o.stamp : codegen ', buildrules)
-        self.assertIn('build lto.main/foo.o.stamp : codegen ', buildrules)
-      # Check that main does not call foo.
-      disasm = subprocess.check_output(['llvm-objdump', '-d', 'main'])
-      main_idx = disasm.index(b' <main>:\n')
-      after_main_idx = disasm.index(b'\n\n', main_idx)
-      main_disasm = disasm[main_idx:after_main_idx]
-      self.assertNotIn(b'foo', main_disasm)
-
-  def test_generate_no_codegen(self):
-    with named_directory() as d, working_directory(d):
-      with open('main.o', 'wb') as f:
-        f.write(b'\7fELF')
-      with mock.patch('sys.stderr', new_callable=StringIO) as stderr:
-        rc = RemoteLinkUnixAllowMain().main([
-            'remote_ld.py', '--ar-path',
-            self.llvmar(), '--generate', '--',
-            self.clangxx(), 'main.o', '-o', 'main'
-        ])
-        self.assertEqual(rc, 5)
-        self.assertIn('no ninja file generated.\n', stderr.getvalue())
-
-  def test_generate(self):
-    with named_directory() as d, working_directory(d):
-      with open('main.o', 'wb') as f:
-        f.write(b'BC\xc0\xde')
-      with mock.patch('sys.stderr', new_callable=StringIO) as stderr:
-        rc = RemoteLinkUnixAllowMain().main([
-            'remote_ld.py', '--ar-path',
-            self.llvmar(), '--generate', '--',
-            self.clangxx(), 'main.o', '-o', 'main'
-        ])
-        self.assertEqual(rc, 0)
-        m = re.search('ninja file (.*)', stderr.getvalue())
-        self.assertIsNotNone(m)
-        path = shlex.split(m.group(1))[0]
-        self.assertTrue(os.path.exists(path))
-        content = open(path).read()
-        self.assertRegex(
-            content,
-            re.compile('^build [^:]+/main\\.o\\.stamp : codegen ',
-                       re.MULTILINE))
-
-  def test_override_allowlist(self):
-    with named_directory() as d, working_directory(d):
-      _create_inputs(d)
-      subprocess.check_call([
-          self.clangxx(), '-c', '-Os', '-flto=thin', 'main.cpp', '-o', 'main.o'
-      ])
-      subprocess.check_call(
-          [self.clangxx(), '-c', '-Os', '-flto=thin', 'foo.cpp', '-o', 'foo.o'])
-      rc = remote_ld.RemoteLinkUnix().main([
-          'remote_ld.py', '--ar-path',
-          self.llvmar(), '--generate', '--allowlist', '--',
-          self.clangxx(), '-fuse-ld=lld', '-flto=thin', 'main.o', 'foo.o', '-o',
-          'main'
-      ])
-      # Should succeed.
-      self.assertEqual(rc, 0)
-      # build.ninja file should have rules for main and foo.
-      ninjafile = os.path.join(d, 'lto.main', 'build.ninja')
-      self.assertTrue(os.path.exists(ninjafile))
-      with open(ninjafile) as f:
-        buildrules = f.read()
-        self.assertIn('build lto.main/main.o.stamp : codegen ', buildrules)
-        self.assertIn('build lto.main/foo.o.stamp : codegen ', buildrules)
-
-
-if __name__ == '__main__':
-  unittest.main()
diff --git a/tools/clang/scripts/remote_link_test_utils.py b/tools/clang/scripts/remote_link_test_utils.py
deleted file mode 100644
index 7e320d49..0000000
--- a/tools/clang/scripts/remote_link_test_utils.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2020 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Utility classes for testing remote_link.
-
-import contextlib
-import os
-import shutil
-import tempfile
-
-
-# tempfile.NamedDirectory is in Python 3.8. This is for compatibility with
-# older Python versions.
-@contextlib.contextmanager
-def named_directory(*args, **kwargs):
-  name = tempfile.mkdtemp(*args, **kwargs)
-  try:
-    yield name
-  finally:
-    shutil.rmtree(name)
-
-
-@contextlib.contextmanager
-def working_directory(newcwd):
-  """
-  Changes working directory to the specified directory, runs enclosed code,
-  and changes back to the previous directory.
-  """
-  oldcwd = os.getcwd()
-  os.chdir(newcwd)
-  try:
-    # Use os.getcwd() instead of newcwd so that we have a path that works
-    # inside the block.
-    yield os.getcwd()
-  finally:
-    os.chdir(oldcwd)
diff --git a/tools/clang/scripts/remote_link_unit_tests.py b/tools/clang/scripts/remote_link_unit_tests.py
deleted file mode 100755
index aac44f0a..0000000
--- a/tools/clang/scripts/remote_link_unit_tests.py
+++ /dev/null
@@ -1,244 +0,0 @@
-#! /usr/bin/env python3
-# Copyright 2020 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Unit tests for remote_link.
-#
-# Usage:
-#
-#   tools/clang/scripts/remote_link_unit_tests.py
-#
-# A coverage report combining these tests with the integration tests
-# in remote_link_integration_tests.py can be generated by running:
-#
-#   env COVERAGE_FILE=.coverage.unit python3 third_party/pycoverage run \
-#     tools/clang/scripts/remote_link_unit_tests.py
-#   env COVERAGE_FILE=.coverage.integration python3 third_party/pycoverage \
-#     run tools/clang/scripts/remote_link_integration_tests.py
-#   python3 third_party/pycoverage combine
-#   python3 third_party/pycoverage html
-#
-# The report will be available as htmlcov/index.html
-
-import remote_ld
-import remote_link
-
-import os
-import unittest
-from unittest import mock
-
-from remote_link_test_utils import named_directory, working_directory
-
-
-class FakeFs(object):
-  """
-  Context manager that mocks the functions through which remote_link
-  interacts with the filesystem.
-  """
-
-  def __init__(self, bitcode_files=None, other_files=None):
-    self.bitcode_files = set(bitcode_files or [])
-    self.other_files = set(other_files or [])
-
-    def ensure_file(path):
-      self.other_files.add(path)
-
-    def exists(path):
-      return path in self.bitcode_files or path in self.other_files
-
-    def is_bitcode_file(path):
-      return path in self.bitcode_files
-
-    self.mock_ensure_file = mock.patch('remote_link.ensure_file', ensure_file)
-    self.mock_exists = mock.patch('os.path.exists', exists)
-    self.mock_is_bitcode_file = mock.patch('remote_link.is_bitcode_file',
-                                           is_bitcode_file)
-
-  def __enter__(self):
-    self.mock_ensure_file.start()
-    self.mock_exists.start()
-    self.mock_is_bitcode_file.start()
-    return self
-
-  def __exit__(self, exnty, *args, **kwargs):
-    self.mock_is_bitcode_file.stop()
-    self.mock_exists.stop()
-    self.mock_ensure_file.stop()
-    return exnty is None
-
-
-class RemoteLinkUnitTest(unittest.TestCase):
-  """
-  Unit tests for remote_link.
-  """
-
-  def test_analyze_expanded_args_nocodegen(self):
-    with FakeFs(other_files=['foo.o', 'bar.o']):
-      self.assertIsNone(remote_ld.RemoteLinkUnix().analyze_expanded_args(
-          ['clang', 'foo.o', 'bar.o', '-o', 'foo'], 'foo', 'clang', 'lto.foo',
-          'common', False))
-
-  def test_analyze_expanded_args_one_codegen(self):
-    with FakeFs(bitcode_files=['foo.o'], other_files=['bar.o']):
-      result = remote_ld.RemoteLinkUnix().analyze_expanded_args(
-          ['clang', 'foo.o', 'bar.o', '-o', 'foo'], 'foo', 'clang', 'lto.foo',
-          'common', False)
-      self.assertIsNotNone(result)
-      self.assertNotEqual(len(result.codegen), 0)
-      self.assertEqual(result.codegen[0][1], 'foo.o')
-      self.assertEqual(len(result.codegen), 1)
-      self.assertIn('foo.o', result.index_params)
-      self.assertIn('bar.o', result.index_params)
-      self.assertIn('bar.o', result.final_params)
-      # foo.o should not be in final_params because it will be added via
-      # the used object file.
-      self.assertNotIn('foo.o', result.final_params)
-
-  def test_analyze_expanded_args_params(self):
-    with FakeFs(bitcode_files=['foo.o']):
-      result = remote_ld.RemoteLinkUnix().analyze_expanded_args([
-          'clang', '-O2', '--target=arm-none-eabi', '-march=armv7-a',
-          '-flto=thin', '-fsplit-lto-unit', '-fwhole-program-vtables',
-          '-fsanitize=cfi', '-g', '-gsplit-dwarf', '-mllvm',
-          '-generate-type-units', 'foo.o', '-o', 'foo'
-      ], 'foo', 'clang', 'lto.foo', 'common', False)
-      self.assertIsNotNone(result)
-      self.assertIn('-Wl,-plugin-opt=obj-path=lto.foo/foo.split.o',
-                    result.index_params)
-      self.assertIn('-O2', result.index_params)
-      self.assertIn('--target=arm-none-eabi', result.codegen_params)
-      self.assertIn('-march=armv7-a', result.codegen_params)
-      self.assertIn('-g', result.index_params)
-      self.assertIn('-gsplit-dwarf', result.index_params)
-      self.assertIn('-mllvm -generate-type-units',
-                    ' '.join(result.index_params))
-      self.assertIn('-flto=thin', result.index_params)
-      self.assertIn('-fwhole-program-vtables', result.index_params)
-      self.assertIn('-fsanitize=cfi', result.index_params)
-
-      self.assertIn('-O2', result.codegen_params)
-      self.assertIn('--target=arm-none-eabi', result.codegen_params)
-      self.assertIn('-march=armv7-a', result.codegen_params)
-      self.assertIn('-gsplit-dwarf', result.codegen_params)
-      self.assertIn('-mllvm -generate-type-units',
-                    ' '.join(result.codegen_params))
-      self.assertNotIn('-flto=thin', result.codegen_params)
-      self.assertNotIn('-fwhole-program-vtables', result.codegen_params)
-      self.assertNotIn('-fsanitize=cfi', result.codegen_params)
-
-      self.assertIn('-flto=thin', result.final_params)
-
-  def test_codegen_params_default(self):
-    with FakeFs(bitcode_files=['foo.o'], other_files=['bar.o']):
-      result = remote_ld.RemoteLinkUnix().analyze_expanded_args(
-          ['clang', 'foo.o', 'bar.o', '-o', 'foo'], 'foo', 'clang', 'lto.foo',
-          'common', False)
-      # Codegen optimization level should default to 2.
-      self.assertIn('-O2', result.codegen_params)
-      # -fdata-sections and -ffunction-sections default to on to match the
-      # behavior of local linking.
-      self.assertIn('-fdata-sections', result.codegen_params)
-      self.assertIn('-ffunction-sections', result.codegen_params)
-
-  def test_codegen_params_default_cl(self):
-    with FakeFs(bitcode_files=['foo.obj'], other_files=['bar.obj']):
-      result = remote_link.RemoteLinkWindows().analyze_expanded_args(
-          ['clang-cl', 'foo.obj', 'bar.obj', '-Fefoo.exe'], 'foo.exe',
-          'clang-cl', 'lto.foo', 'common', False)
-      # Codegen optimization level should default to 2.
-      self.assertIn('-O2', result.codegen_params)
-      # -Gw and -Gy default to on to match the behavior of local linking.
-      self.assertIn('-Gw', result.codegen_params)
-      self.assertIn('-Gy', result.codegen_params)
-
-  def test_codegen_params_no_data_sections(self):
-    with FakeFs(bitcode_files=['foo.o'], other_files=['bar.o']):
-      result = remote_ld.RemoteLinkUnix().analyze_expanded_args(
-          ['clang', '-fno-data-sections', 'foo.o', 'bar.o', '-o', 'foo'], 'foo',
-          'clang', 'lto.foo', 'common', False)
-      self.assertNotIn('-fdata-sections', result.codegen_params)
-      self.assertIn('-ffunction-sections', result.codegen_params)
-
-  def test_codegen_params_no_function_sections(self):
-    with FakeFs(bitcode_files=['foo.o'], other_files=['bar.o']):
-      result = remote_ld.RemoteLinkUnix().analyze_expanded_args(
-          ['clang', '-fno-function-sections', 'foo.o', 'bar.o', '-o', 'foo'],
-          'foo', 'clang', 'lto.foo', 'common', False)
-      self.assertIn('-fdata-sections', result.codegen_params)
-      self.assertNotIn('-ffunction-sections', result.codegen_params)
-
-  def test_codegen_params_no_data_sections_cl(self):
-    with FakeFs(bitcode_files=['foo.obj'], other_files=['bar.obj']):
-      result = remote_link.RemoteLinkWindows().analyze_expanded_args(
-          ['clang-cl', '/Gw-', 'foo.obj', 'bar.obj', '/Fefoo.exe'], 'foo.exe',
-          'clang-cl', 'lto.foo', 'common', False)
-      self.assertNotIn('-fdata-sections', result.codegen_params)
-      self.assertNotIn('-Gw', result.codegen_params)
-      self.assertNotIn('/Gw', result.codegen_params)
-      self.assertIn('-Gy', result.codegen_params)
-
-  def test_codegen_params_no_function_sections_cl(self):
-    with FakeFs(bitcode_files=['foo.obj'], other_files=['bar.obj']):
-      result = remote_link.RemoteLinkWindows().analyze_expanded_args(
-          ['clang-cl', '/Gy-', 'foo.obj', 'bar.obj', '/Fefoo.exe'], 'foo.exe',
-          'clang-cl', 'lto.foo', 'common', False)
-      self.assertIn('-Gw', result.codegen_params)
-      self.assertNotIn('-ffunction-sections', result.codegen_params)
-      self.assertNotIn('-Gy', result.codegen_params)
-      self.assertNotIn('/Gy', result.codegen_params)
-
-  def test_codegen_params_explicit_data_and_function_sections(self):
-    with FakeFs(bitcode_files=['foo.o'], other_files=['bar.o']):
-      result = remote_ld.RemoteLinkUnix().analyze_expanded_args([
-          'clang', '-ffunction-sections', '-fdata-sections', 'foo.o', 'bar.o',
-          '-o', 'foo'
-      ], 'foo', 'clang', 'lto.foo', 'common', False)
-      self.assertIn('-fdata-sections', result.codegen_params)
-      self.assertIn('-ffunction-sections', result.codegen_params)
-
-  def test_codegen_params_explicit_data_and_function_sections_cl(self):
-    with FakeFs(bitcode_files=['foo.obj'], other_files=['bar.obj']):
-      result = remote_link.RemoteLinkWindows().analyze_expanded_args(
-          ['clang-cl', '/Gy', '-Gw', 'foo.obj', 'bar.obj', '/Fefoo.exe'],
-          'foo.exe', 'clang-cl', 'lto.foo', 'common', False)
-      self.assertIn('-Gw', result.codegen_params)
-      self.assertIn('/Gy', result.codegen_params)
-      self.assertNotIn('-fdata-sections', result.codegen_params)
-      self.assertNotIn('-ffunction-sections', result.codegen_params)
-
-  def test_ensure_file_no_dir(self):
-    with named_directory() as d, working_directory(d):
-      self.assertFalse(os.path.exists('test'))
-      remote_link.ensure_file('test')
-      self.assertTrue(os.path.exists('test'))
-
-  def test_ensure_file_existing(self):
-    with named_directory() as d, working_directory(d):
-      self.assertFalse(os.path.exists('foo/test'))
-      remote_link.ensure_file('foo/test')
-      self.assertTrue(os.path.exists('foo/test'))
-      os.utime('foo/test', (0, 0))
-      statresult = os.stat('foo/test')
-      remote_link.ensure_file('foo/test')
-      self.assertTrue(os.path.exists('foo/test'))
-      newstatresult = os.stat('foo/test')
-      self.assertEqual(newstatresult.st_mtime, statresult.st_mtime)
-
-  def test_ensure_file_error(self):
-    with named_directory() as d, working_directory(d):
-      self.assertFalse(os.path.exists('test'))
-      remote_link.ensure_file('test')
-      self.assertTrue(os.path.exists('test'))
-      self.assertRaises(OSError, remote_link.ensure_file, 'test/impossible')
-
-  def test_transform_codegen_param_on_mllvm(self):
-    # Regression test for crbug.com/1135234
-    link = remote_ld.RemoteLinkUnix()
-    self.assertEqual(
-        link.transform_codegen_param_common('-mllvm,-import-instr-limit=20'),
-        ['-mllvm', '-import-instr-limit=20'])
-
-
-if __name__ == '__main__':
-  unittest.main()
diff --git a/tools/clang/scripts/sync_deps.py b/tools/clang/scripts/sync_deps.py
index 79871b8..283cdc2 100755
--- a/tools/clang/scripts/sync_deps.py
+++ b/tools/clang/scripts/sync_deps.py
@@ -79,7 +79,7 @@
   with tempfile.NamedTemporaryFile() as f:
     DownloadUrl(url, f)
     f.seek(0)
-    sha256sum = hashlib.file_digest(f, 'sha256').hexdigest()
+    sha256sum = hashlib.sha256(f.read()).hexdigest()
 
   return f'{object_name},{sha256sum},{size_bytes},{generation}'
 
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 967bcfc..b3c1431 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -40,7 +40,7 @@
 # should not be changed manually.
 # They are also read by build/config/compiler/BUILD.gn.
 CLANG_REVISION = 'llvmorg-22-init-8940-g4d4cb757'
-CLANG_SUB_REVISION = 1
+CLANG_SUB_REVISION = 84
 
 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION)
 RELEASE_VERSION = '22'
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index dd44a6f..72cad6c 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -515,7 +515,7 @@
     "includes": [4380],
   },
   "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/new_tab_footer/resources.grd": {
-    "META": {"sizes": {"includes": [10]}},
+    "META": {"sizes": {"includes": [15]}},
     "includes": [4390],
   },
   "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/new_tab_page/resources.grd": {
diff --git a/tools/json_schema_compiler/test/web_idl/basics.idl b/tools/json_schema_compiler/test/web_idl/basics.idl
index 6f54c17e..502b83d 100644
--- a/tools/json_schema_compiler/test/web_idl/basics.idl
+++ b/tools/json_schema_compiler/test/web_idl/basics.idl
@@ -143,11 +143,6 @@
   "name2"
 };
 
-[nodoc] enum EnumTypeWithNoDoc {
-  "name1",
-  "name2"
-};
-
 partial dictionary Manifest {
   // Description of a manifest key.
   required DOMString string_key;
diff --git a/tools/json_schema_compiler/test/web_idl/deprecated.idl b/tools/json_schema_compiler/test/web_idl/deprecated_examples.idl
similarity index 81%
rename from tools/json_schema_compiler/test/web_idl/deprecated.idl
rename to tools/json_schema_compiler/test/web_idl/deprecated_examples.idl
index 0f0fdf0..1188b06 100644
--- a/tools/json_schema_compiler/test/web_idl/deprecated.idl
+++ b/tools/json_schema_compiler/test/web_idl/deprecated_examples.idl
@@ -7,8 +7,12 @@
   "name2"
 };
 
-dictionary DictWithDeprecatedMember {
-  [deprecated="This dict member is deprecated"] boolean deprecatedDictMember;
+[deprecated="This dict is deprecated"] dictionary DeprecatedDictionary {
+  boolean normalMember;
+};
+
+dictionary DictionaryWithDeprecatedMember {
+  [deprecated="This dict member is deprecated"] boolean deprecatedMember;
 };
 
 callback OnDeprecatedEventListener = undefined ();
@@ -27,6 +31,9 @@
 
   [deprecated="This event is deprecated"]
   static attribute OnDeprecatedEvent onDeprecatedEvent;
+
+  [deprecated="This property is deprecated"]
+  const long DEPRECATED_PROPERTY = 6;
 };
 
 partial interface Browser {
diff --git a/tools/json_schema_compiler/test/web_idl/nodoc_examples.idl b/tools/json_schema_compiler/test/web_idl/nodoc_examples.idl
new file mode 100644
index 0000000..eec0cfd
--- /dev/null
+++ b/tools/json_schema_compiler/test/web_idl/nodoc_examples.idl
@@ -0,0 +1,56 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+dictionary NormalDictionary {
+  DOMString normalMember;
+};
+
+[nodoc] dictionary DictionaryWithNoDoc {
+  DOMString memberOnNoDocDict;
+};
+
+dictionary DictionaryWithNoDocMember {
+  [nodoc] DOMString nodocMember;
+  DOMString normalMember;
+};
+
+enum NormalEnum {
+  "name1",
+  "name2"
+};
+
+[nodoc] enum EnumWithNoDoc {
+  "name1",
+  "name2"
+};
+
+callback SharedEventListener = undefined ();
+interface NoDocEvent : ExtensionEvent {
+  static void addListener(SharedEventListener listener);
+  static void removeListener(SharedEventListener listener);
+  static void hasListener(SharedEventListener listener);
+};
+interface NormalEvent : ExtensionEvent {
+  static void addListener(SharedEventListener listener);
+  static void removeListener(SharedEventListener listener);
+  static void hasListener(SharedEventListener listener);
+};
+
+// The nodoc API. This exists to demonstrate a variety of nodoc extended
+// attribute usage.
+[nodoc] interface NoDocAPI {
+
+  [nodoc] static undefined functionWithNoDoc();
+  static undefined normalFunction();
+
+  [nodoc] static attribute NoDocEvent noDocEvent;
+  static attribute NormalEvent normalEvent;
+
+  [nodoc] const long PROPERTY_WITH_NODOC = 6;
+  const long NORMAL_PROPERTY = 6;
+};
+
+partial interface Browser {
+  static attribute NoDocAPI nodocAPI;
+};
diff --git a/tools/json_schema_compiler/test/web_idl/nodoc_on_namespace.idl b/tools/json_schema_compiler/test/web_idl/nodoc_on_namespace.idl
deleted file mode 100644
index 88d56d7..0000000
--- a/tools/json_schema_compiler/test/web_idl/nodoc_on_namespace.idl
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// The nodoc API. This exists to demonstrate nodoc on the main interface itself.
-[nodoc] interface NodocAPI {
-
-  // This is a valid function, but since the interface is marked nodoc, it wont
-  // be documented.
-  static undefined fooFunction();
-};
-
-partial interface Browser {
-  static attribute NodocAPI nodocAPI;
-};
diff --git a/tools/json_schema_compiler/web_idl_schema.py b/tools/json_schema_compiler/web_idl_schema.py
index 9cc45304..dc4bb82 100755
--- a/tools/json_schema_compiler/web_idl_schema.py
+++ b/tools/json_schema_compiler/web_idl_schema.py
@@ -132,6 +132,25 @@
   return None
 
 
+def AddCommonExtendedAttributeProperties(node: IDLNode, properties: dict):
+  """Looks for common extended attributes and adds them to properties.
+
+  Several different nodes in our IDL schemas have a common set of extended
+  attributes which they all share. This helper function looks for them and adds
+  the associated values to the supplied properties if they are present.
+
+  Args:
+    node: The IDLNode to look for the extended attributes on.
+    properties: The object to add the associated key value pairs to.
+  """
+  if deprecated := GetExtendedAttributeValue(node, 'deprecated'):
+    properties['deprecated'] = deprecated
+  if HasExtendedAttribute(node, 'nodoc'):
+    properties['nodoc'] = True
+  # TODO(crbug.com/340297705): Add the nocompile extended attribute here too and
+  # test for it on associated nodes.
+
+
 def _ExtractNodeComment(node: IDLNode) -> str:
   """Extract contiguous file comments above a node and return them as a string.
 
@@ -537,8 +556,7 @@
     if not self.node.GetProperty('REQUIRED'):
       self.properties['optional'] = True
 
-    if deprecated := GetExtendedAttributeValue(self.node, 'deprecated'):
-      self.properties['deprecated'] = deprecated
+    AddCommonExtendedAttributeProperties(self.node, self.properties)
 
     description = ProcessNodeDescription(self.node).description
     if description:
@@ -569,8 +587,7 @@
     if (description_data.description):
       properties['description'] = description_data.description
 
-    if deprecated := GetExtendedAttributeValue(self.node, 'deprecated'):
-      properties['deprecated'] = deprecated
+    AddCommonExtendedAttributeProperties(self.node, properties)
 
     parameters = []
     arguments_node = self.node.GetOneOf('Arguments')
@@ -636,6 +653,8 @@
         'properties': properties,
         'type': 'object'
     }
+    AddCommonExtendedAttributeProperties(self.node, result)
+
     return result
 
 
@@ -666,10 +685,7 @@
         'type': 'string',
         'enum': enum
     }
-    if HasExtendedAttribute(self.node, 'nodoc'):
-      result['nodoc'] = True
-    if deprecated := GetExtendedAttributeValue(self.node, 'deprecated'):
-      result['deprecated'] = deprecated
+    AddCommonExtendedAttributeProperties(self.node, result)
 
     return result
 
@@ -731,9 +747,7 @@
           FunctionArgument(argument, parameter_descriptions).Process())
     properties['parameters'] = parameters
 
-    if deprecated := GetExtendedAttributeValue(self.node, 'deprecated'):
-      properties['deprecated'] = deprecated
-
+    AddCommonExtendedAttributeProperties(self.node, properties)
 
     return properties
 
@@ -805,8 +819,7 @@
     if (description_data.description):
       properties['description'] = description_data.description
 
-    # TODO(crbug.com/445495198): Add support for appropriate extended attributes
-    # on properties (deprecated, nodoc, nocompile).
+    AddCommonExtendedAttributeProperties(self.node, properties)
 
     return (self.node.GetName(), properties)
 
@@ -898,32 +911,33 @@
       property_key, property_value = Property(node).process()
       properties[property_key] = property_value
 
-    # Several special attributes specific to the schema compilation process are
-    # defined using Extended Attributes on the API Interface definition.
-    nodoc = HasExtendedAttribute(self.namespace, 'nodoc')
-    platforms = GetExtendedAttributeValue(self.namespace, 'platforms')
-    deprecated = GetExtendedAttributeValue(self.namespace, 'deprecated')
-    compiler_options = {}
-    if implemented_in := GetExtendedAttributeValue(self.namespace,
-                                                   'implemented_in'):
-      compiler_options['implemented_in'] = implemented_in
-    if HasExtendedAttribute(self.namespace, 'generate_error_messages'):
-      compiler_options['generate_error_messages'] = True
-
-    return {
+    result = {
         'namespace': self.name,
         'functions': functions,
         'types': types,
         'events': events,
         'properties': properties,
         'manifest_keys': manifest_keys,
-        'nodoc': nodoc,
         'description': description,
-        'platforms': platforms,
-        'compiler_options': compiler_options,
-        'deprecated': deprecated,
+        'nodoc': False,
+        'platforms': None,
+        'deprecated': None,
+        'compiler_options': {},
     }
 
+    # Several special attributes specific to the schema compilation process are
+    # defined using Extended Attributes on the API Interface definition.
+    AddCommonExtendedAttributeProperties(self.namespace, result)
+    if platforms := GetExtendedAttributeValue(self.namespace, 'platforms'):
+      result['platforms'] = platforms
+    if implemented_in := GetExtendedAttributeValue(self.namespace,
+                                                   'implemented_in'):
+      result['compiler_options']['implemented_in'] = implemented_in
+    if HasExtendedAttribute(self.namespace, 'generate_error_messages'):
+      result['compiler_options']['generate_error_messages'] = True
+
+    return result
+
 
 class IDLSchema:
   """Holds the entirety of a parsed IDL schema, ready to process further.
diff --git a/tools/json_schema_compiler/web_idl_schema_test.py b/tools/json_schema_compiler/web_idl_schema_test.py
index d09d4e7..6c57cac9 100755
--- a/tools/json_schema_compiler/web_idl_schema_test.py
+++ b/tools/json_schema_compiler/web_idl_schema_test.py
@@ -50,6 +50,19 @@
   raise KeyError('Could not find "type" with id "%s" in schema' % name)
 
 
+def getProperty(schema: dict, name: str) -> dict:
+  """Gets the property dictionary with the specified name from the schema.
+
+  Args:
+    schema: The processed API schema dictionary to look for the property in.
+    name: The name of the property to look for.
+
+  Returns:
+    The dictionary for the property with the specified name.
+  """
+  return schema['properties'][name]
+
+
 def getEvent(schema: dict, name: str) -> dict:
   """Gets the event dictionary with the specified name from the schema.
 
@@ -523,7 +536,6 @@
   # 'properties' object.
   def testConstantProperties(self):
     schema = self.idl_basics
-    properties = schema['properties']
     # Properties are an ordered dict, so ordering matches order in the IDL file.
     self.assertEqual(
         [
@@ -532,28 +544,26 @@
             'CONSTANT_STRING',
             'DESCRIBED_CONSTANT',
         ],
-        list(properties.keys()),
+        list(schema['properties'].keys()),
     )
     self.assertEqual({
         'type': 'integer',
         'value': 39
-    }, properties['CONSTANT_LONG'])
+    }, getProperty(schema, 'CONSTANT_LONG'))
     self.assertEqual({
         'type': 'number',
         'value': 3.9
-    }, properties['CONSTANT_DOUBLE'])
+    }, getProperty(schema, 'CONSTANT_DOUBLE'))
     self.assertEqual({
         'type': 'string',
         'value': 'Foo'
-    }, properties['CONSTANT_STRING'])
+    }, getProperty(schema, 'CONSTANT_STRING'))
     self.assertEqual(
         {
             'type': 'integer',
             'value': 9,
             'description': 'Comment on a constant property with a value.',
-        },
-        properties['DESCRIBED_CONSTANT'],
-    )
+        }, getProperty(schema, 'DESCRIBED_CONSTANT'))
 
   # Tests that a const DOMString defined on an API Interface which is missing
   # the StringValue extended attribute will throw an error. It's unfortunate
@@ -744,31 +754,59 @@
         'test/web_idl/void_unsupported.idl',
     )
 
-  # Tests that an API interface that uses the nodoc extended attribute has the
-  # related nodoc attribute set to true after processing.
-  def testNoDocOnNamespace(self):
-    idl = web_idl_schema.Load('test/web_idl/nodoc_on_namespace.idl')
+  # Tests that the nodoc extended attribute used in various places gets the
+  # related attribute set to True after processing.
+  def testNoDocExtendedAttribute(self):
+    idl = web_idl_schema.Load('test/web_idl/nodoc_examples.idl')
     self.assertEqual(1, len(idl))
     schema = idl[0]
+
+    # Top level namespace:
     self.assertEqual('nodocAPI', schema['namespace'])
     self.assertTrue(schema['nodoc'])
     # Also ensure the description comes through correctly on the node with
     # 'nodoc' as an extended attribute.
     self.assertEqual(
-        'The nodoc API. This exists to demonstrate nodoc on the main interface'
-        ' itself.',
+        'The nodoc API. This exists to demonstrate a variety of nodoc extended'
+        ' attribute usage.',
         schema['description'],
     )
 
-  # Tests that an enum that uses the nodoc extended attribute has the related
-  # nodoc attribute set to true after processing.
-  def testNoDocOnEnum(self):
-    schema = self.idl_basics
-    nodoc_enum = getType(schema, 'EnumTypeWithNoDoc')
-    self.assertEqual(True, nodoc_enum['nodoc'])
-    # An enum without the extended attribute will not have a nodoc attribute.
-    no_nodoc_enum = getType(schema, 'EnumType')
-    self.assertFalse(hasattr(no_nodoc_enum, 'nodoc'))
+    # Enums:
+    nodoc_enum = getType(schema, 'EnumWithNoDoc')
+    self.assertTrue(nodoc_enum['nodoc'])
+    normal_enum = getType(schema, 'NormalEnum')
+    self.assertFalse(hasattr(normal_enum, 'nodoc'))
+
+    # Dictionaries:
+    nodoc_dict = getType(schema, 'DictionaryWithNoDoc')
+    self.assertTrue(nodoc_dict['nodoc'])
+    normal_dict = getType(schema, 'NormalDictionary')
+    self.assertFalse(hasattr(normal_dict, 'nodoc'))
+
+    # Dictionary members:
+    nodoc_dict_member = getType(schema, 'DictionaryWithNoDocMember')
+    self.assertTrue(nodoc_dict_member['properties']['nodocMember']['nodoc'])
+    self.assertFalse(
+        hasattr(nodoc_dict_member['properties']['normalMember'], 'nodoc'))
+
+    # Functions:
+    nodoc_function = getFunction(schema, 'functionWithNoDoc')
+    self.assertTrue(nodoc_function['nodoc'])
+    normal_function = getFunction(schema, 'normalFunction')
+    self.assertFalse(hasattr(normal_function, 'nodoc'))
+
+    # Events:
+    nodoc_event = getEvent(schema, 'noDocEvent')
+    self.assertTrue(nodoc_event['nodoc'])
+    normal_event = getEvent(schema, 'normalEvent')
+    self.assertFalse(hasattr(normal_event, 'nodoc'))
+
+    # Properties:
+    nodoc_property = getProperty(schema, 'PROPERTY_WITH_NODOC')
+    self.assertTrue(nodoc_property['nodoc'])
+    normal_property = getProperty(schema, 'NORMAL_PROPERTY')
+    self.assertFalse(hasattr(normal_property, 'nodoc'))
 
   # Tests that the deprecated extended attribute used in various places get the
   # related attribute set to the provided string after processing.
@@ -776,20 +814,28 @@
   # attributes preceding them, so we need to find some other way to mark a
   # specific enum value as deprecated.
   def testDeprecatedExtendedAttribute(self):
-    idl = web_idl_schema.Load('test/web_idl/deprecated.idl')
+    idl = web_idl_schema.Load('test/web_idl/deprecated_examples.idl')
     self.assertEqual(1, len(idl))
     schema = idl[0]
 
+    # Top level Namespace:
     self.assertEqual('This API is deprecated', schema['deprecated'])
 
+    # Enums:
     deprecated_enum = getType(schema, 'DeprecatedEnum')
     self.assertEqual('This enum is deprecated', deprecated_enum['deprecated'])
 
-    deprecated_dict = getType(schema, 'DictWithDeprecatedMember')
+    # Dictionaries:
+    deprecated_dict = getType(schema, 'DeprecatedDictionary')
+    self.assertEqual('This dict is deprecated', deprecated_dict['deprecated'])
+
+    # Dictionary members:
+    deprecated_dict_member = getType(schema, 'DictionaryWithDeprecatedMember')
     self.assertEqual(
         'This dict member is deprecated',
-        deprecated_dict['properties']['deprecatedDictMember']['deprecated'])
+        deprecated_dict_member['properties']['deprecatedMember']['deprecated'])
 
+    # Functions:
     deprecated_function = getFunction(schema, 'deprecatedFunction')
     self.assertEqual(
         'This function is deprecated and it has such a long message that\n  it'
@@ -797,9 +843,15 @@
         deprecated_function['deprecated'],
     )
 
+    # Events:
     deprecated_event = getEvent(schema, 'onDeprecatedEvent')
     self.assertEqual('This event is deprecated', deprecated_event['deprecated'])
 
+    # Properties:
+    deprecated_property = getProperty(schema, 'DEPRECATED_PROPERTY')
+    self.assertEqual('This property is deprecated',
+                     deprecated_property['deprecated'])
+
   # Tests that a function defined with the requiredCallback extended attribute
   # does not have the returns_async field marked as optional after processing.
   # Note: These are only relevant to contexts which don't support promise based
diff --git a/tools/metrics/BUILD.gn b/tools/metrics/BUILD.gn
index 86ce94bc..da3e8126 100644
--- a/tools/metrics/BUILD.gn
+++ b/tools/metrics/BUILD.gn
@@ -95,10 +95,10 @@
     "//tools/metrics/private_metrics/dwa_builders_template.py",
     "//tools/metrics/private_metrics/dwa_decode_template.py",
     "//tools/metrics/private_metrics/dwa.xml",
-    "//tools/metrics/private_metrics/codegen.py",
     "//tools/metrics/private_metrics/dwa_model.py",
-    "//tools/metrics/private_metrics/gen_builders.py",
-    "//tools/metrics/private_metrics/gen_builders_test.py",
+    "//tools/metrics/private_metrics/gen_private_metrics_builders.py",
+    "//tools/metrics/private_metrics/gen_private_metrics_builders_test.py",
+    "//tools/metrics/private_metrics/private_metrics_codegen.py",
     "//tools/metrics/private_metrics/private_metrics_model_shared_test.py",
     "//tools/metrics/private_metrics/private_metrics_validations.py",
     "//tools/metrics/private_metrics/private_metrics_validations_test.py",
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 7f674b8..456deb0 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -35979,6 +35979,16 @@
   </description>
 </action>
 
+<action name="SafeBrowsing.Settings.ShowedFromTipsNotificationsPromo">
+  <owner>bjfong@google.com</owner>
+  <owner>wylieb@google.com</owner>
+  <owner>clank-tab-dev@google.com</owner>
+  <description>
+    The Safe Browsing setting page is shown to the user. The user reaches the
+    page from the tips notifications promo.
+  </description>
+</action>
+
 <action name="SafeBrowsing.Settings.StandardProtectionClicked">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f01ac99..a8c4a3b 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2523,7 +2523,7 @@
   <int value="121" label="Pointer lock"/>
   <int value="122" label="Abusive notification site permissions"/>
   <int value="123" label="Tracking Protection"/>
-  <int value="124" label="Top-level 3PCD Origin Trial"/>
+  <int value="124" label="(Obsolete) Top-level 3PCD Origin Trial"/>
   <int value="125" label="Display Media System Audio"/>
   <int value="126" label="Javascript optimizer"/>
   <int value="127" label="Storage Access Header Origin Trial"/>
@@ -13816,7 +13816,6 @@
   <int value="-122492389" label="enable-browser-task-scheduler"/>
   <int value="-122458532" label="FilesExtractArchive:enabled"/>
   <int value="-121563330" label="SecurePaymentConfirmationBrowser:disabled"/>
-  <int value="-121476972" label="ContentSettingsPartitioning:disabled"/>
   <int value="-120988078" label="OmniboxGroupingFrameworkForZPS:enabled"/>
   <int value="-120521482" label="DirectManipulationStylus:enabled"/>
   <int value="-120430245" label="EnableExpKitTextClassifier:enabled"/>
@@ -16744,7 +16743,6 @@
       label="QuickAnswersAlwaysTriggerForSingleWord:disabled"/>
   <int value="920563204" label="AndroidHatsRefactor:enabled"/>
   <int value="921561616" label="WebAssemblyTiering:disabled"/>
-  <int value="923359298" label="ContentSettingsPartitioning:enabled"/>
   <int value="923752712" label="WelcomeTour:enabled"/>
   <int value="923923308" label="WifiSyncAllowDeletes:enabled"/>
   <int value="923947923" label="ReadPrinterCapabilitiesWithXps:disabled"/>
@@ -21397,6 +21395,7 @@
   <int value="194" label="Chrome App deprecation"/>
   <int value="196" label="Spotlight session start countdown"/>
   <int value="197" label="Face Gaze Active"/>
+  <int value="201" label="Incompatible Charger"/>
 </enum>
 
 <enum name="NQEObservationSource">
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index 286e7084..dfafd9df 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -2273,7 +2273,7 @@
 </histogram>
 
 <histogram name="Accessibility.LiveTranslate.TranslationDispatcher.LoadResult"
-    enum="TranslationDispatcherLoadResult" expires_after="2026-02-01">
+    enum="TranslationDispatcherLoadResult" expires_after="2026-04-05">
   <owner>evanliu@google.com</owner>
   <owner>chrome-accessibility-eng@google.com</owner>
   <summary>
@@ -2283,7 +2283,7 @@
 </histogram>
 
 <histogram name="Accessibility.LiveTranslate.TranslationDispatcher.ParseResult"
-    enum="TranslationDispatcherParseResult" expires_after="2026-02-01">
+    enum="TranslationDispatcherParseResult" expires_after="2026-04-05">
   <owner>evanliu@google.com</owner>
   <owner>chrome-accessibility-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/android/enums.xml b/tools/metrics/histograms/metadata/android/enums.xml
index 69654c1b..c183faf 100644
--- a/tools/metrics/histograms/metadata/android/enums.xml
+++ b/tools/metrics/histograms/metadata/android/enums.xml
@@ -1989,40 +1989,40 @@
 
 <enum name="WebPageContentProviderEvent">
   <int value="0" label="GET_CONTENT_URI_FAILED">
-    WebPageContentProvider failed to generate a URI for the current web page,
-    nothing will be attached to AssistContent
+    WebPageContentProvider failed to generate a JSON string with URIs for the
+    current web page, nothing will be attached to AssistContent
   </int>
-  <int value="1" label="QUERY">
+  <int value="1" label="REQUEST_STARTED">
     WebPageContentProvider received a query by an external app
   </int>
-  <int value="2" label="QUERY_FAILED_CURRENT_TAB_CHANGED">
+  <int value="2" label="REQUEST_FAILED_CURRENT_TAB_CHANGED">
     WebPageContentProvider returned an empty cursor while processing a query
     because the current tab no longer matches
   </int>
-  <int value="3" label="QUERY_FAILED_INVALID_URI">
+  <int value="3" label="REQUEST_FAILED_INVALID_URI">
     WebPageContentProvider threw an error while processing a query because it
     had an invalid URI
   </int>
-  <int value="4" label="QUERY_FAILED_INVALID_ID">
+  <int value="4" label="REQUEST_FAILED_INVALID_ID">
     WebPageContentProvider returned an empty cursor while processing a query
     because the URI had an invalid id (e.g. non existing or no longer valid).
   </int>
-  <int value="5" label="QUERY_FAILED_TO_GET_CURRENT_TAB">
+  <int value="5" label="REQUEST_FAILED_TO_GET_CURRENT_TAB">
     WebPageContentProvider returned an empty cursor while processing a query
     because we failed to get the current tab from the browser process
   </int>
-  <int value="6" label="QUERY_SUCCEEDED_ALREADY_EXTRACTING (Deprecated)">
+  <int value="6" label="REQUEST_SUCCEEDED_ALREADY_EXTRACTING (Deprecated)">
     WebPageContentProvider returned a cursor with a pending result for a page
     extraction task that was already running
   </int>
-  <int value="7" label="QUERY_SUCCEEDED_RETURNED_EXTRACTED">
+  <int value="7" label="REQUEST_SUCCEEDED_RETURNED_EXTRACTED">
     WebPageContentProvider returned a cursor with page contents successfully
   </int>
-  <int value="8" label="QUERY_SUCCEEDED_STARTED_EXTRACTION (Deprecated)">
+  <int value="8" label="REQUEST_SUCCEEDED_STARTED_EXTRACTION (Deprecated)">
     WebPageContentProvider successfully started a page extraction task and
     returned a cursor with a pending result
   </int>
-  <int value="9" label="TEXT_EXTRACTION_FAILED_EMPTY_RESULT">
+  <int value="9" label="REQUEST_FAILED_EMPTY_RESULT">
     WebPageContentProvider's page extraction task failed to execute
   </int>
   <int value="10" label="TEXT_EXTRACTION_FAILED_TAB_CHANGED (Deprecated)">
@@ -2033,17 +2033,24 @@
     WebPageContentProvider's page extraction task completed successfully and
     observers were notified
   </int>
-  <int value="12" label="TIMEOUT">
+  <int value="12" label="URI_INVALIDATED_TIMEOUT">
     The last URI generated by WebPageContentProvider was not queried on time and
     it's no longer valid.
   </int>
-  <int value="13" label="QUERY_FAILED_INTERRUPTED">
+  <int value="13" label="REQUEST_FAILED_INTERRUPTED">
     Asynchronous text extraction task was interrupted, query call failed.
   </int>
-  <int value="14" label="QUERY_FAILED_TIMED_OUT">
+  <int value="14" label="REQUEST_FAILED_TIMED_OUT">
     Asynchronous text extraction task timed out, query call failed.
   </int>
-  <int value="15" label="QUERY_FAILED_EXCEPTION">
+  <int value="15" label="REQUEST_FAILED_EXCEPTION">
+    An exception was thrown while extracting text, query call failed.
+  </int>
+  <int value="16" label="GET_CONTENT_URI_SUCCESS">
+    PageContentProvider generated a JSON string with URIs for the current web
+    page to be attached to AssistContent.
+  </int>
+  <int value="17" label="URI_INVALIDATED_NEW_REQUEST">
     An exception was thrown while extracting text, query call failed.
   </int>
 </enum>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 083d61bc..ae06aa01 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -429,7 +429,7 @@
 </histogram>
 
 <histogram name="Android.AdaptiveToolbarButton.Clicked"
-    enum="AdaptiveToolbarButtonVariant" expires_after="2026-02-01">
+    enum="AdaptiveToolbarButtonVariant" expires_after="2026-04-05">
   <owner>bttk@chromium.org</owner>
   <owner>chrome-segmentation-platform@google.com</owner>
   <summary>
@@ -438,7 +438,7 @@
 </histogram>
 
 <histogram name="Android.AdaptiveToolbarButton.SessionVariant"
-    enum="AdaptiveToolbarButtonVariant" expires_after="2026-02-01">
+    enum="AdaptiveToolbarButtonVariant" expires_after="2026-04-05">
   <owner>bttk@chromium.org</owner>
   <owner>chrome-segmentation-platform@google.com</owner>
   <summary>
@@ -998,7 +998,7 @@
 </histogram>
 
 <histogram name="Android.BackPress.IncorrectEdgeSwipe" enum="SwipeEdge"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>eleanorlee@google.com</owner>
   <owner>lazzzis@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
@@ -1011,7 +1011,7 @@
 </histogram>
 
 <histogram name="Android.BackPress.IncorrectEdgeSwipe.CountChained"
-    units="count" expires_after="2026-02-01">
+    units="count" expires_after="2026-04-05">
   <owner>eleanorlee@google.com</owner>
   <owner>lazzzis@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
@@ -2055,7 +2055,7 @@
 </histogram>
 
 <histogram name="Android.EdgeToEdge.BackupNavbarInsets.EdgeToEdgeController"
-    enum="BackupNavbarInsetsSource" expires_after="2026-02-01">
+    enum="BackupNavbarInsetsSource" expires_after="2026-04-05">
   <owner>clhager@google.com</owner>
   <owner>wenyufu@chromium.org</owner>
   <owner>edge-to-edge@chromium.org</owner>
@@ -2139,7 +2139,7 @@
 </histogram>
 
 <histogram name="Android.EdgeToEdge.NavigationBarMandatoryGesturesMismatch"
-    enum="HasSeenNonZeroNavBarState" expires_after="2026-02-01">
+    enum="HasSeenNonZeroNavBarState" expires_after="2026-04-05">
   <owner>clhager@google.com</owner>
   <owner>wenyufu@chromium.org</owner>
   <owner>edge-to-edge@chromium.org</owner>
@@ -2157,7 +2157,7 @@
 </histogram>
 
 <histogram name="Android.EdgeToEdge.SupportedConfigurationSwitch2"
-    enum="SupportedConfigurationSwitch" expires_after="2026-02-01">
+    enum="SupportedConfigurationSwitch" expires_after="2026-04-05">
   <owner>clhager@google.com</owner>
   <owner>wenyufu@chromium.org</owner>
   <owner>edge-to-edge@chromium.org</owner>
@@ -2656,7 +2656,7 @@
 </histogram>
 
 <histogram name="Android.InputOnViz.Viz.DroppedKeyEvent.Type"
-    enum="AInputEventType" expires_after="2026-01-18">
+    enum="AInputEventType" expires_after="2026-04-05">
   <owner>kartarsingh@google.com</owner>
   <owner>woa-performance-team@google.com</owner>
   <summary>
@@ -2787,7 +2787,7 @@
 </histogram>
 
 <histogram name="Android.Intent.InterceptNavigationDeferDuration" units="ms"
-    expires_after="2026-01-29">
+    expires_after="2026-04-05">
   <owner>mthiesse@chromium.org</owner>
   <owner>yfriedman@chromium.org</owner>
   <summary>
@@ -2922,7 +2922,7 @@
 </histogram>
 
 <histogram name="Android.LibraryLoader.Prefetch.Duration" units="ms"
-    expires_after="2026-01-30">
+    expires_after="2026-04-05">
   <owner>ziadyoussef@chromium.org</owner>
   <owner>woa-performance@google.com</owner>
   <owner>src/android_webview/OWNERS</owner>
@@ -3289,7 +3289,7 @@
 
 <histogram
     name="Android.MultiInstance.NumActivities.DesktopWindow.{InstanceAllocationType}"
-    units="activities" expires_after="2026-02-01">
+    units="activities" expires_after="2026-04-05">
   <owner>aishwaryarj@google.com</owner>
   <owner>wenyufu@chromium.org</owner>
   <owner>clank-large-form-factors@google.com</owner>
@@ -3435,7 +3435,7 @@
 </histogram>
 
 <histogram name="Android.MultiWindowMode.Configuration" enum="WindowingMode"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>aishwaryarj@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -4450,7 +4450,7 @@
 </histogram>
 
 <histogram name="Android.Pdf.AssistContent.IsEnterpriseInfoCached"
-    enum="Boolean" expires_after="2026-01-11">
+    enum="Boolean" expires_after="2026-04-05">
   <owner>shuyng@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -4483,7 +4483,7 @@
 </histogram>
 
 <histogram name="Android.Pdf.DocumentLoadInterval" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>shuyng@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -5051,7 +5051,7 @@
 </histogram>
 
 <histogram name="Android.RestoreResult" enum="AndroidRestoreResult"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>triploblastic@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
@@ -5356,7 +5356,7 @@
 </histogram>
 
 <histogram name="Android.StyleUtils.FontLoadingOutcome"
-    enum="FontLoadingOutcome" expires_after="2026-02-01">
+    enum="FontLoadingOutcome" expires_after="2026-04-05">
   <owner>wenyufu@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
   <summary>
@@ -8631,7 +8631,7 @@
 
 <histogram
     name="Android.WebView.Startup.CreationTime.FirstInstanceAfterGlobalStartup"
-    units="ms" expires_after="2025-12-31">
+    units="ms" expires_after="2026-04-05">
   <owner>torne@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -8732,7 +8732,7 @@
 </histogram>
 
 <histogram name="Android.WebView.Startup.CreationTime.NotFirstInstance"
-    units="ms" expires_after="2025-12-31">
+    units="ms" expires_after="2026-04-05">
   <owner>torne@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -8938,7 +8938,7 @@
 </histogram>
 
 <histogram name="Android.WebView.StorageAccessRelation2"
-    enum="WebViewStorageAccessAppRelation" expires_after="2026-02-01">
+    enum="WebViewStorageAccessAppRelation" expires_after="2026-04-05">
   <owner>bewise@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml
index 3cc2ce5a..e57a4b8 100644
--- a/tools/metrics/histograms/metadata/arc/histograms.xml
+++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -1448,7 +1448,7 @@
 </histogram>
 
 <histogram name="Arc.KeyMint.VerifiedBootHash.Result"
-    enum="ArcVerifiedBootHashResult" expires_after="2026-02-01">
+    enum="ArcVerifiedBootHashResult" expires_after="2026-04-05">
   <owner>batoon@google.com</owner>
   <owner>arc-commercial@google.com</owner>
   <summary>
@@ -1458,7 +1458,7 @@
 </histogram>
 
 <histogram name="Arc.KeyMint.VerifiedBootKey.Result"
-    enum="ArcVerifiedBootKeyResult" expires_after="2026-02-01">
+    enum="ArcVerifiedBootKeyResult" expires_after="2026-04-05">
   <owner>batoon@google.com</owner>
   <owner>arc-commercial@google.com</owner>
   <summary>
@@ -1468,7 +1468,7 @@
 </histogram>
 
 <histogram name="Arc.KeyMint.VerifiedBootState.Result"
-    enum="ArcVerifiedBootStateResult" expires_after="2026-02-01">
+    enum="ArcVerifiedBootStateResult" expires_after="2026-04-05">
   <owner>batoon@google.com</owner>
   <owner>arc-commercial@google.com</owner>
   <summary>
@@ -2473,7 +2473,7 @@
 </histogram>
 
 <histogram name="Arc.VmDataMigration.BatteryConsumption" units="%"
-    expires_after="2025-12-31">
+    expires_after="2026-04-05">
   <owner>momohatt@google.com</owner>
   <owner>youkichihosoi@google.com</owner>
   <owner>arc-storage@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index e0dc29c..ccbf133 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -4761,7 +4761,7 @@
 </histogram>
 
 <histogram name="Ash.Login.Lock.AuthMethod.Used.ClamShellMode"
-    enum="AuthMethod" expires_after="2026-02-01">
+    enum="AuthMethod" expires_after="2026-04-05">
   <owner>emaamari@google.com</owner>
   <owner>cros-lurs@google.com</owner>
   <summary>
@@ -4897,7 +4897,7 @@
 </histogram>
 
 <histogram name="Ash.Login.ShowGaiaSignin.WaitTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>longbowei@google.com</owner>
   <owner>zhangwenyu@google.com</owner>
   <owner>cros-families-eng@google.com</owner>
@@ -4981,7 +4981,7 @@
 </histogram>
 
 <histogram name="Ash.LoginPerf.AutoRestore.AllBrowserWindowsPresented"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>hidehiko@chromium.org</owner>
   <owner>junis@google.com</owner>
   <owner>cros-sw-perf@google.org</owner>
@@ -4995,7 +4995,7 @@
 </histogram>
 
 <histogram name="Ash.LoginPerf.AutoRestore.AllBrowserWindowsShown" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hidehiko@chromium.org</owner>
   <owner>junis@google.com</owner>
   <owner>cros-sw-perf@google.org</owner>
@@ -5008,7 +5008,7 @@
 </histogram>
 
 <histogram name="Ash.LoginPerf.ManualRestore.{UIFlow}.TotalDuration" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hidehiko@chromium.org</owner>
   <owner>junis@google.com</owner>
   <owner>zxdan@chromium.org</owner>
@@ -5036,7 +5036,7 @@
 </histogram>
 
 <histogram name="Ash.LoginPerf.{RestoreMode}.AllShelfIconsLoaded" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hidehiko@chromium.org</owner>
   <owner>junis@google.com</owner>
   <owner>cros-sw-perf@google.org</owner>
@@ -5060,7 +5060,7 @@
 </histogram>
 
 <histogram name="Ash.LoginPerf.{RestoreMode}.DeferredTasksStarted" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hidehiko@chromium.org</owner>
   <owner>junis@google.com</owner>
   <owner>cros-sw-perf@google.org</owner>
@@ -5073,7 +5073,7 @@
 
 <histogram
     name="Ash.LoginPerf.{RestoreMode}.PostLoginAnimation.Duration.{TabletOrClamshellMode}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>hidehiko@chromium.org</owner>
   <owner>junis@google.com</owner>
   <owner>cros-sw-perf@google.org</owner>
@@ -5101,7 +5101,7 @@
 
 <histogram
     name="Ash.LoginPerf.{RestoreMode}.PostLoginAnimation.Smoothness.{TabletOrClamshellMode}"
-    units="%" expires_after="2026-02-01">
+    units="%" expires_after="2026-04-05">
   <owner>hidehiko@chromium.org</owner>
   <owner>junis@google.com</owner>
   <owner>cros-sw-perf@google.org</owner>
@@ -5126,7 +5126,7 @@
 </histogram>
 
 <histogram name="Ash.LoginPerf.{RestoreMode}.TotalDuration" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hidehiko@chromium.org</owner>
   <owner>junis@google.com</owner>
   <owner>cros-sw-perf@google.org</owner>
@@ -5138,7 +5138,7 @@
 </histogram>
 
 <histogram name="Ash.LoginReadahead.Result" enum="LoginReadaheadResult"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hidehiko@chromium.org</owner>
   <owner>junis@google.com</owner>
   <owner>cros-sw-perf@google.com</owner>
@@ -5492,7 +5492,7 @@
 </histogram>
 
 <histogram name="Ash.Notification.ClearAllVisible.AnimationSmoothness"
-    units="%" expires_after="2026-02-01">
+    units="%" expires_after="2026-04-05">
   <owner>leandre@chromium.org</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -5843,7 +5843,7 @@
 </histogram>
 
 <histogram name="Ash.NotifierFramework.Nudge.ShownCount"
-    enum="NudgeCatalogName" expires_after="2026-02-01">
+    enum="NudgeCatalogName" expires_after="2026-04-05">
   <owner>kradtke@chromium.org</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -5926,7 +5926,7 @@
 </histogram>
 
 <histogram name="Ash.NotifierFramework.Toast.ShownCount"
-    enum="ToastCatalogName" expires_after="2026-02-01">
+    enum="ToastCatalogName" expires_after="2026-04-05">
   <owner>kradtke@chromium.org</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
@@ -6457,7 +6457,7 @@
 </histogram>
 
 <histogram name="Ash.Personalization.Ambient.GooglePhotosPreviewsLoadTime"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>cowmoo@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7554,7 +7554,7 @@
 </histogram>
 
 <histogram name="Ash.ScannerFeature.Timer.{Type}" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>jhtin@google.com</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>
@@ -7592,7 +7592,7 @@
 </histogram>
 
 <histogram name="Ash.ScannerFeature.UserState" enum="ScannerFeatureUserState"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>jhtin@google.com</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>
@@ -7665,7 +7665,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.CreateButton" enum="SeaPenTemplateId"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ericamlee@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7675,7 +7675,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.Freeform.Api.Thumbnails.Count" units="count"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>thuongphan@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7685,7 +7685,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.Freeform.Api.Wallpaper.HasImage" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>thuongphan@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7695,7 +7695,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.Freeform.Api.{Type}.Latency" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>thuongphan@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7707,7 +7707,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.Freeform.Api.{Type}.MantaStatusCode"
-    enum="MantaStatusCode" expires_after="2026-02-01">
+    enum="MantaStatusCode" expires_after="2026-04-05">
   <owner>thuongphan@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7719,7 +7719,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.Freeform.Api.{Type}.Timeout" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>thuongphan@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7729,7 +7729,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.Freeform.SamplePrompt.SampleClicked"
-    enum="SeaPenSamplePromptId" expires_after="2026-02-01">
+    enum="SeaPenSamplePromptId" expires_after="2026-04-05">
   <owner>ericamlee@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7738,7 +7738,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.Freeform.SamplePrompt.Shuffle.Clicked"
-    enum="BooleanClicked" expires_after="2026-02-01">
+    enum="BooleanClicked" expires_after="2026-04-05">
   <owner>ericamlee@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7747,7 +7747,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.Freeform.Suggestion.Clicked" enum="BooleanClicked"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ericamlee@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7756,7 +7756,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.Freeform.Suggestion.Shuffle.Clicked"
-    enum="BooleanClicked" expires_after="2026-02-01">
+    enum="BooleanClicked" expires_after="2026-04-05">
   <owner>ericamlee@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7765,7 +7765,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.Freeform.Tab.Clicked" enum="SeaPenFreeformTab"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ericamlee@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>Emitted when the user clicks on a freeform tab.</summary>
@@ -7783,7 +7783,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.Freeform.{AppName}.Visited" enum="BooleanHit"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ericamlee@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7794,7 +7794,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.Freeform.{AppName}.{Source}.ImageSet"
-    enum="BooleanHit" expires_after="2026-02-01">
+    enum="BooleanHit" expires_after="2026-04-05">
   <owner>thuongphan@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7816,7 +7816,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.ThumbnailClicked" enum="SeaPenTemplateId"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>pzliu@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7847,7 +7847,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.{AppName}.Visited" enum="BooleanHit"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>pzliu@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
@@ -7870,7 +7870,7 @@
 </histogram>
 
 <histogram name="Ash.SeaPen.{TemplateName}.UserFeedback" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>pzliu@google.com</owner>
   <owner>cros-p13n-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/attribution_reporting/histograms.xml b/tools/metrics/histograms/metadata/attribution_reporting/histograms.xml
index 95c002b7..543b8bd5 100644
--- a/tools/metrics/histograms/metadata/attribution_reporting/histograms.xml
+++ b/tools/metrics/histograms/metadata/attribution_reporting/histograms.xml
@@ -814,7 +814,7 @@
 </histogram>
 
 <histogram name="Conversions.NumTriggerStates" units="states"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>tquintanilla@chromium.org</owner>
   <owner>apaseltiner@chromium.org</owner>
   <owner>measurement-api-dev+metrics@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index a65210b..ea9d87b6 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -892,7 +892,7 @@
 </histogram>
 
 <histogram name="Autofill.AddedNewAddress"
-    enum="AutofillManuallyAddedAddressSurface" expires_after="2026-02-01">
+    enum="AutofillManuallyAddedAddressSurface" expires_after="2026-04-05">
   <owner>brunobraga@google.com</owner>
   <owner>vykochko@google.com</owner>
   <owner>chrome-autofill-alerts@google.com</owner>
@@ -1548,7 +1548,7 @@
 </histogram>
 
 <histogram name="Autofill.AndroidAutofillAvailabilityStatus"
-    enum="AndroidAutofillAvailabilityStatus" expires_after="2026-02-01">
+    enum="AndroidAutofillAvailabilityStatus" expires_after="2026-04-05">
   <owner>friedrichh@chromium.org</owner>
   <owner>chrome-autofill-alerts@google.com</owner>
   <summary>
@@ -3940,7 +3940,7 @@
 
 <histogram
     name="Autofill.Funnel.NotClassifiedAsTargetFilling.FillAfterSuggestion.Password"
-    enum="BooleanAutofillFillAfterSuggestion" expires_after="2026-02-01">
+    enum="BooleanAutofillFillAfterSuggestion" expires_after="2026-04-05">
   <owner>brunobraga@google.com</owner>
   <owner>chrome-autofill-alerts@google.com</owner>
   <summary>
@@ -4761,7 +4761,7 @@
 
 <histogram
     name="Autofill.ManualFallback.ExplicitlyTriggered.ClassifiedAsTargetFilling.Password"
-    enum="BooleanSelectManualFallback" expires_after="2026-02-01">
+    enum="BooleanSelectManualFallback" expires_after="2026-04-05">
   <owner>brunobraga@google.com</owner>
   <owner>chrome-autofill-alerts@google.com</owner>
   <summary>
@@ -4775,7 +4775,7 @@
 
 <histogram
     name="Autofill.ManualFallback.ExplicitlyTriggered.NotClassifiedAsTargetFilling.Password"
-    enum="BooleanSelectManualFallback" expires_after="2026-02-01">
+    enum="BooleanSelectManualFallback" expires_after="2026-04-05">
   <owner>brunobraga@google.com</owner>
   <owner>chrome-autofill-alerts@google.com</owner>
   <summary>
@@ -5005,7 +5005,7 @@
 </histogram>
 
 <histogram name="Autofill.OtpInputDialog.{CardType}.{OtpAuthType}.Result"
-    enum="AutofillOtpInputDialogResult" expires_after="2026-02-01">
+    enum="AutofillOtpInputDialogResult" expires_after="2026-04-05">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -5835,7 +5835,7 @@
 <!-- LINT.ThenChange(/components/autofill/core/browser/metrics/profile_import_metrics.cc:GetImportTypeMetricsString) -->
 
 <histogram name="Autofill.ProfileImportValidCandidate.ZipCode.Length"
-    units="units" expires_after="2026-02-01">
+    units="units" expires_after="2026-04-05">
   <owner>mmaryia@google.com</owner>
   <owner>chrome-autofill-alerts@google.com</owner>
   <summary>
@@ -5846,7 +5846,7 @@
 </histogram>
 
 <histogram name="Autofill.ProfileImportValidCandidate.ZipCode.Separator"
-    enum="AutofillZipCodeSeparators" expires_after="2026-02-01">
+    enum="AutofillZipCodeSeparators" expires_after="2026-04-05">
   <owner>mmaryia@google.com</owner>
   <owner>chrome-autofill-alerts@google.com</owner>
   <summary>
@@ -6155,7 +6155,7 @@
 </histogram>
 
 <histogram name="Autofill.ResetAutofillPrefToChrome" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>friedrichh@chromium.org</owner>
   <owner>chrome-autofill-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/blink/enums.xml b/tools/metrics/histograms/metadata/blink/enums.xml
index e4df3ed..b4c5f1e 100644
--- a/tools/metrics/histograms/metadata/blink/enums.xml
+++ b/tools/metrics/histograms/metadata/blink/enums.xml
@@ -2279,8 +2279,10 @@
   <int value="1971" label="AnchorClickDispatchForNonConnectedNode"/>
   <int value="1972" label="HTMLParseErrorNestedForm"/>
   <int value="1973" label="FontShapingNotDefGlyphObserved"/>
-  <int value="1974" label="PostMessageOutgoingWouldBeBlockedByConnectSrc"/>
-  <int value="1975" label="PostMessageIncomingWouldBeBlockedByConnectSrc"/>
+  <int value="1974"
+      label="OBSOLETE_PostMessageOutgoingWouldBeBlockedByConnectSrc"/>
+  <int value="1975"
+      label="OBSOLETE_PostMessageIncomingWouldBeBlockedByConnectSrc"/>
   <int value="1977" label="CrossOriginPropertyAccess"/>
   <int value="1978" label="CrossOriginPropertyAccessFromOpener"/>
   <int value="1979" label="CredentialManagerCreate"/>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 7ae7a9a..44e8c41 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -121,7 +121,7 @@
 </histogram>
 
 <histogram name="Blink.Accessibility.ModeFlag" enum="AccessibilityModeFlagEnum"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>kevers@chromium.org</owner>
   <owner>chrome-a11y-core@chromium.org</owner>
   <summary>
@@ -245,7 +245,7 @@
 </histogram>
 
 <histogram name="Blink.Animation.CompositedAnimationFailureReason"
-    enum="CompositorAnimationsFailureReason" expires_after="2026-02-01">
+    enum="CompositorAnimationsFailureReason" expires_after="2026-04-05">
   <owner>smcgruer@chromium.org</owner>
   <owner>animations-dev@chromium.org</owner>
   <summary>
@@ -1787,7 +1787,7 @@
 </histogram>
 
 <histogram name="Blink.FedCm.IdpSigninStatus.ShowPopupWindowResult"
-    enum="FedCmShowPopupWindowResult" expires_after="2025-12-12">
+    enum="FedCmShowPopupWindowResult" expires_after="2026-04-05">
   <owner>tanzachary@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
   <summary>
@@ -3660,7 +3660,7 @@
 </histogram>
 
 <histogram name="Blink.LCPP.PotentiallyLCPResourcePriorityBoosts2"
-    units="count" expires_after="2026-02-01">
+    units="count" expires_after="2026-04-05">
   <owner>kouhei@chromium.org</owner>
   <owner>
     src/third_party/blink/renderer/core/lcp_critical_path_predictor/OWNERS
@@ -4218,7 +4218,7 @@
 
 <histogram
     name="Blink.MemoryCache.TotalTakenTimeForDidLoadResourceFromMemoryCache"
-    enum="RevalidationPolicy" expires_after="2026-02-28">
+    units="microseconds" expires_after="2026-02-28">
   <owner>sisidovski@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -4477,7 +4477,7 @@
 </histogram>
 
 <histogram name="Blink.PreloadRequestStartDuration" units="microseconds"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>sisidovski@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -5179,7 +5179,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.CrossDeviceFailure"
-    enum="WebOTPCrossDeviceFailure" expires_after="2025-12-12">
+    enum="WebOTPCrossDeviceFailure" expires_after="2026-04-05">
   <owner>yigu@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/bookmarks/histograms.xml b/tools/metrics/histograms/metadata/bookmarks/histograms.xml
index 9849d10..cb39446 100644
--- a/tools/metrics/histograms/metadata/bookmarks/histograms.xml
+++ b/tools/metrics/histograms/metadata/bookmarks/histograms.xml
@@ -449,7 +449,7 @@
 </histogram>
 
 <histogram name="Bookmarks.MobileBookmarkManager.SortOptionUsed"
-    enum="MobileBookmarkManagerBookmarkRowSortOrder" expires_after="2026-01-15">
+    enum="MobileBookmarkManagerBookmarkRowSortOrder" expires_after="2026-04-05">
   <owner>wylieb@google.com</owner>
   <owner>chrome-collections@google.com</owner>
   <summary>
@@ -561,7 +561,7 @@
 </histogram>
 
 <histogram name="Bookmarks.ParentFolderType" enum="BookmarkFolderType"
-    expires_after="2026-01-31">
+    expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>top-chrome-desktop-ui@google.com</owner>
   <summary>
@@ -808,7 +808,7 @@
 </histogram>
 
 <histogram name="Bookmarks.UserFolder.OnProfileLoad.BookmarkBarTopLevelItems"
-    units="items" expires_after="2026-02-01">
+    units="items" expires_after="2026-04-05">
   <owner>yzshen@chromium.org</owner>
   <owner>mschillaci@google.com</owner>
   <owner>eleanorlee@google.com</owner>
@@ -825,7 +825,7 @@
 </histogram>
 
 <histogram name="Bookmarks.UserFolder.OnProfileLoad.Count" units="folders"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>yzshen@chromium.org</owner>
   <owner>chrome-collections@google.com</owner>
   <summary>
@@ -838,7 +838,7 @@
 </histogram>
 
 <histogram name="Bookmarks.UserFolder.OnProfileLoad.TopLevelCount"
-    units="folders" expires_after="2026-02-01">
+    units="folders" expires_after="2026-04-05">
   <owner>yzshen@chromium.org</owner>
   <owner>chrome-collections@google.com</owner>
   <summary>
@@ -851,7 +851,7 @@
 </histogram>
 
 <histogram name="Bookmarks.UserFolderDepth.UrlAdded" units="folders"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>yzshen@chromium.org</owner>
   <owner>chrome-collections@google.com</owner>
   <summary>
@@ -940,7 +940,7 @@
 </histogram>
 
 <histogram name="PowerBookmarks.SidePanel.BookmarksShown" units="Bookmarks"
-    expires_after="2026-01-31">
+    expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>chrome-desktop-sea@google.com</owner>
   <component>1457140</component>
@@ -952,7 +952,7 @@
 </histogram>
 
 <histogram name="PowerBookmarks.SidePanel.Search.CTR"
-    enum="BookmarksSidePanelSearchCTREvent" expires_after="2026-01-31">
+    enum="BookmarksSidePanelSearchCTREvent" expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>chrome-desktop-sea@google.com</owner>
   <component>1457140</component>
@@ -964,7 +964,7 @@
 </histogram>
 
 <histogram name="PowerBookmarks.SidePanel.SearchOrFilter.BookmarksShown"
-    units="Bookmarks" expires_after="2026-01-31">
+    units="Bookmarks" expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>chrome-desktop-sea@google.com</owner>
   <component>1457140</component>
@@ -976,7 +976,7 @@
 </histogram>
 
 <histogram name="PowerBookmarks.SidePanel.SortTypeShown"
-    enum="BookmarksSidePanelSortType" expires_after="2026-01-31">
+    enum="BookmarksSidePanelSortType" expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>chrome-desktop-sea@google.com</owner>
   <component>1457140</component>
@@ -987,7 +987,7 @@
 </histogram>
 
 <histogram name="PowerBookmarks.SidePanel.ViewTypeShown"
-    enum="BookmarksSidePanelViewType" expires_after="2026-01-31">
+    enum="BookmarksSidePanelViewType" expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>chrome-desktop-sea@google.com</owner>
   <component>1457140</component>
diff --git a/tools/metrics/histograms/metadata/borealis/histograms.xml b/tools/metrics/histograms/metadata/borealis/histograms.xml
index 0060dc4b..ea9d754 100644
--- a/tools/metrics/histograms/metadata/borealis/histograms.xml
+++ b/tools/metrics/histograms/metadata/borealis/histograms.xml
@@ -128,7 +128,7 @@
 </histogram>
 
 <histogram name="Borealis.EngagementTime.{Variant}" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>philpearson@google.com</owner>
   <owner>src/chrome/browser/ash/borealis/OWNERS</owner>
   <summary>
@@ -201,7 +201,7 @@
 </histogram>
 
 <histogram name="Borealis.Install.Result" enum="BorealisInstallResult"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>philpearson@google.com</owner>
   <owner>src/chrome/browser/ash/borealis/OWNERS</owner>
   <summary>
@@ -224,7 +224,7 @@
 </histogram>
 
 <histogram name="Borealis.Launch.Source" enum="BorealisLaunchSource"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>philpearson@google.com</owner>
   <owner>src/chrome/browser/ash/borealis/OWNERS</owner>
   <summary>
@@ -322,7 +322,7 @@
 </histogram>
 
 <histogram name="Borealis.Startup.TimeToFirstWindow" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>philpearson@google.com</owner>
   <owner>src/chrome/browser/ash/borealis/OWNERS</owner>
   <improvement direction="LOWER_IS_BETTER"/>
diff --git a/tools/metrics/histograms/metadata/browser/histograms.xml b/tools/metrics/histograms/metadata/browser/histograms.xml
index 3b2cf803..70ebc680 100644
--- a/tools/metrics/histograms/metadata/browser/histograms.xml
+++ b/tools/metrics/histograms/metadata/browser/histograms.xml
@@ -292,7 +292,7 @@
 </histogram>
 
 <histogram name="Browser.ERP.EventEnqueueResult"
-    enum="EnterpriseCloudReportingStatusCode" expires_after="2026-02-01">
+    enum="EnterpriseCloudReportingStatusCode" expires_after="2026-04-05">
   <owner>djzhang@google.com</owner>
   <owner>jdudder@google.com</owner>
   <owner>cros-reporting-team@google.com</owner>
@@ -851,7 +851,7 @@
 </histogram>
 
 <histogram name="Browser.PaintPreview.Capture.CompressedOnDiskSize" units="KB"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ckitagawa@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
   <owner>chrome-fdt@google.com</owner>
@@ -921,7 +921,7 @@
 </histogram>
 
 <histogram name="Browser.PaintPreview.Player.TimeToFirstBitmap" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ckitagawa@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
   <owner>chrome-fdt@google.com</owner>
@@ -989,7 +989,7 @@
 </histogram>
 
 <histogram name="Browser.PaintPreview.TabbedPlayer.TimeToFirstBitmap"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>ckitagawa@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
   <owner>chrome-fdt@google.com</owner>
@@ -1029,7 +1029,7 @@
 </histogram>
 
 <histogram name="Browser.PaintPreview.TabService.DiskUsageAtStartup" units="KB"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ckitagawa@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
   <summary>
@@ -1193,7 +1193,7 @@
 
 <histogram
     name="BrowserRenderProcessHost.AvailableMemory.SpareRenderer.{ProcessType}ProcessKillWithin{Time}"
-    units="MB" expires_after="2026-01-15">
+    units="MB" expires_after="2026-04-05">
   <owner>gjc@google.com</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -1790,7 +1790,7 @@
 </histogram>
 
 <histogram name="BrowserServices.VerificationResult"
-    enum="BrowserServicesVerificationResult" expires_after="2026-02-01">
+    enum="BrowserServicesVerificationResult" expires_after="2026-04-05">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -1803,7 +1803,7 @@
 </histogram>
 
 <histogram name="BrowserServices.VerificationTime.Offline" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -1817,7 +1817,7 @@
 </histogram>
 
 <histogram name="BrowserServices.VerificationTime.Online" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -2106,7 +2106,7 @@
   </summary>
 </histogram>
 
-<histogram name="SidePanel.ResizedWidth" units="px" expires_after="2026-01-31">
+<histogram name="SidePanel.ResizedWidth" units="px" expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>top-chrome-desktop-ui@google.com</owner>
   <summary>
@@ -2116,7 +2116,7 @@
 </histogram>
 
 <histogram name="SidePanel.ResizedWidthPercentage" units="% of window width"
-    expires_after="2026-01-31">
+    expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>top-chrome-desktop-ui@google.com</owner>
   <summary>
@@ -2136,7 +2136,7 @@
 </histogram>
 
 <histogram name="SidePanel.{SidePanelEntry}.ResizedWidth" units="px"
-    expires_after="2026-01-31">
+    expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>top-chrome-desktop-ui@google.com</owner>
   <summary>
@@ -2147,7 +2147,7 @@
 </histogram>
 
 <histogram name="SidePanel.{SidePanelEntry}.ResizedWidthPercentage"
-    units="% of window width" expires_after="2026-01-31">
+    units="% of window width" expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>top-chrome-desktop-ui@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/browsing_topics/histograms.xml b/tools/metrics/histograms/metadata/browsing_topics/histograms.xml
index 9059c53..fdc70c17 100644
--- a/tools/metrics/histograms/metadata/browsing_topics/histograms.xml
+++ b/tools/metrics/histograms/metadata/browsing_topics/histograms.xml
@@ -285,7 +285,7 @@
 </histogram>
 
 <histogram name="BrowsingTopics.Result.RealTopicCount"
-    enum="BrowsingTopicsNumberOfTopics" expires_after="2026-02-01">
+    enum="BrowsingTopicsNumberOfTopics" expires_after="2026-04-05">
   <owner>yaoxia@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <summary>
@@ -295,7 +295,7 @@
 </histogram>
 
 <histogram name="BrowsingTopics.Result.Status"
-    enum="BrowsingTopicsApiAccessResult" expires_after="2026-02-01">
+    enum="BrowsingTopicsApiAccessResult" expires_after="2026-04-05">
   <owner>yaoxia@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index 90ec6c0..2c946f6 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -248,7 +248,7 @@
 </variants>
 
 <histogram name="ChromeOS.AnomalousProcCount.AttemptedMemfdExec"
-    units="processes" expires_after="2026-02-01">
+    units="processes" expires_after="2026-04-05">
   <owner>enlightened@chromium.org</owner>
   <owner>chromeos-hardening@google.com</owner>
   <summary>
@@ -264,7 +264,7 @@
 </histogram>
 
 <histogram name="ChromeOS.AnomalousProcCount.ForbiddenIntersection.{Category}"
-    units="processes" expires_after="2026-02-01">
+    units="processes" expires_after="2026-04-05">
   <owner>enlightened@chromium.org</owner>
   <owner>chromeos-hardening@google.com</owner>
   <summary>
@@ -2461,7 +2461,7 @@
 </histogram>
 
 <histogram name="ChromeOS.K12UserAgeClassification.LogSegment"
-    enum="K12UserAgeClassificationMatrix" expires_after="2026-02-01">
+    enum="K12UserAgeClassificationMatrix" expires_after="2026-04-05">
   <owner>zifanzhang@google.com</owner>
   <owner>cros-client-wa@google.com</owner>
   <summary>Tracks the K12 user age classification.</summary>
@@ -3522,7 +3522,7 @@
 </histogram>
 
 <histogram name="ChromeOS.SecurityAnomaly" enum="SecurityAnomaly"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>jorgelo@chromium.org</owner>
   <owner>chromeos-hardening@google.com</owner>
   <summary>
@@ -4952,7 +4952,7 @@
 </histogram>
 
 <histogram name="ChromeOS.WXMountCount" units="count"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>jorgelo@chromium.org</owner>
   <owner>chromeos-hardening@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml b/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml
index 63cc0df..c0654d9 100644
--- a/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos_settings/histograms.xml
@@ -704,7 +704,7 @@
 
 <histogram
     name="ChromeOS.Settings.NumSearchesUntilChange{OsSettingsChangeType}"
-    units="searches" expires_after="2026-02-01">
+    units="searches" expires_after="2026-04-05">
   <owner>wesokuhara@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <owner>cros-settings@google.com</owner>
@@ -969,7 +969,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Settings.TimeUntilChange{OsSettingsChangeType}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>wesokuhara@google.com</owner>
   <owner>xiaohuic@chromium.org</owner>
   <owner>cros-settings@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/commerce/histograms.xml b/tools/metrics/histograms/metadata/commerce/histograms.xml
index 153d12d..35fcd159 100644
--- a/tools/metrics/histograms/metadata/commerce/histograms.xml
+++ b/tools/metrics/histograms/metadata/commerce/histograms.xml
@@ -237,7 +237,7 @@
 </histogram>
 
 <histogram name="Commerce.Discounts.DiscountBubble.TypeOnShow"
-    enum="DiscountClusterType" expires_after="2026-02-01">
+    enum="DiscountClusterType" expires_after="2026-04-05">
   <owner>meiliang@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
   <summary>
@@ -258,7 +258,7 @@
 </histogram>
 
 <histogram name="Commerce.Discounts.DiscountsBubble.TypeOnCopy"
-    enum="DiscountClusterType" expires_after="2026-02-01">
+    enum="DiscountClusterType" expires_after="2026-04-05">
   <owner>meiliang@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
   <summary>
@@ -280,7 +280,7 @@
 </histogram>
 
 <histogram name="Commerce.Discounts.DiscountsBubbleIsAutoShown"
-    enum="BooleanYesNo" expires_after="2026-02-01">
+    enum="BooleanYesNo" expires_after="2026-04-05">
   <owner>meiliang@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
   <summary>
@@ -290,7 +290,7 @@
 </histogram>
 
 <histogram name="Commerce.Discounts.DiscountsPageActionIconIsExpanded"
-    enum="BooleanYesNo" expires_after="2026-02-01">
+    enum="BooleanYesNo" expires_after="2026-04-05">
   <owner>meiliang@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
   <summary>
@@ -332,7 +332,7 @@
 </histogram>
 
 <histogram name="Commerce.Discounts.PageActionIcon.TypeOnShown"
-    enum="DiscountClusterType" expires_after="2026-02-01">
+    enum="DiscountClusterType" expires_after="2026-04-05">
   <owner>meiliang@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
   <summary>
@@ -451,7 +451,7 @@
 </histogram>
 
 <histogram name="Commerce.PDPNavigation.{FeatureName}.Eligible" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ayman@chromium.org</owner>
   <owner>mdjones@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/content/histograms.xml b/tools/metrics/histograms/metadata/content/histograms.xml
index 0b0c2111..8b55816 100644
--- a/tools/metrics/histograms/metadata/content/histograms.xml
+++ b/tools/metrics/histograms/metadata/content/histograms.xml
@@ -80,7 +80,7 @@
 </histogram>
 
 <histogram name="Content.SharedWorker.Host.DestructionSource"
-    enum="SharedWorkerHostDestructionSource" expires_after="2026-02-01">
+    enum="SharedWorkerHostDestructionSource" expires_after="2026-04-05">
   <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-worker@google.org</owner>
   <summary>
@@ -912,7 +912,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.ContentDuplication2.Position{Index}"
-    enum="Boolean" expires_after="2026-02-01">
+    enum="Boolean" expires_after="2026-04-05">
   <owner>jianli@chromium.org</owner>
   <owner>feed@chromium.org</owner>
   <summary>
@@ -991,7 +991,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.DisplayStatusOnOpen"
-    enum="ContentSuggestionsDisplayStatus" expires_after="2026-02-01">
+    enum="ContentSuggestionsDisplayStatus" expires_after="2026-04-05">
   <owner>jianli@chromium.org</owner>
   <owner>feed@chromium.org</owner>
   <summary>
@@ -1187,7 +1187,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.Network.ResponseSizeKB" units="KB"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>jianli@chromium.org</owner>
   <owner>feed@chromium.org</owner>
   <summary>
@@ -1333,7 +1333,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.UploadActionsStatus"
-    enum="FeedUploadActionsStatus" expires_after="2026-02-01">
+    enum="FeedUploadActionsStatus" expires_after="2026-04-05">
   <owner>jianli@chromium.org</owner>
   <owner>feed@chromium.org</owner>
   <summary>
@@ -1366,7 +1366,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.UserActions" enum="FeedUserActionType"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>jianli@chromium.org</owner>
   <owner>feed@chromium.org</owner>
   <summary>
@@ -1956,7 +1956,7 @@
 
 <histogram
     name="ContentSuggestions.{FeedType}.LoadStreamStatus.BackgroundRefresh"
-    enum="FeedLoadStreamStatus" expires_after="2026-02-01">
+    enum="FeedLoadStreamStatus" expires_after="2026-04-05">
   <owner>jianli@chromium.org</owner>
   <owner>feed@chromium.org</owner>
   <summary>
@@ -1974,7 +1974,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.{FeedType}.LoadStreamStatus.Initial"
-    enum="FeedLoadStreamStatus" expires_after="2026-02-01">
+    enum="FeedLoadStreamStatus" expires_after="2026-04-05">
   <owner>jianli@chromium.org</owner>
   <owner>feed@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cookie/histograms.xml b/tools/metrics/histograms/metadata/cookie/histograms.xml
index 4193af2..07c47777 100644
--- a/tools/metrics/histograms/metadata/cookie/histograms.xml
+++ b/tools/metrics/histograms/metadata/cookie/histograms.xml
@@ -290,7 +290,7 @@
 </histogram>
 
 <histogram name="Cookie.EncryptedAndPlaintextValues" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>wfh@chromium.org</owner>
   <owner>src/net/cookies/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cras/histograms.xml b/tools/metrics/histograms/metadata/cras/histograms.xml
index e00e1a1..5708d8e 100644
--- a/tools/metrics/histograms/metadata/cras/histograms.xml
+++ b/tools/metrics/histograms/metadata/cras/histograms.xml
@@ -475,7 +475,7 @@
   <token key="DlcType" variants="CrasDlcType"/>
 </histogram>
 
-<histogram name="Cras.FetchDelayCount" units="count" expires_after="2026-02-01">
+<histogram name="Cras.FetchDelayCount" units="count" expires_after="2026-04-05">
 <!-- Name completed by histogram_suffixes
      name="Cras.ClientType" and
      name="Cras.StreamType" -->
diff --git a/tools/metrics/histograms/metadata/cros_ml/histograms.xml b/tools/metrics/histograms/metadata/cros_ml/histograms.xml
index 40ba8ae..3b554ac 100644
--- a/tools/metrics/histograms/metadata/cros_ml/histograms.xml
+++ b/tools/metrics/histograms/metadata/cros_ml/histograms.xml
@@ -87,7 +87,7 @@
 </histogram>
 
 <histogram name="MachineLearningService.GrammarChecker.Check.Event"
-    enum="Boolean" expires_after="2025-11-09">
+    enum="Boolean" expires_after="2026-04-01">
   <owner>jiwan@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <summary>The result of grammar check, which can be OK or ERROR.</summary>
@@ -95,7 +95,7 @@
 
 <histogram name="MachineLearningService.HandwritingModel.LoadModelResult.Event"
     enum="MachineLearningServiceLoadHandwritingModelResultEvent"
-    expires_after="2025-10-12">
+    expires_after="2026-04-01">
   <owner>amoylan@chromium.org</owner>
   <owner>alanlxl@chromium.org</owner>
   <summary>
@@ -106,7 +106,7 @@
 </histogram>
 
 <histogram name="MachineLearningService.HandwritingModel.Recognize.Event"
-    enum="Boolean" expires_after="2026-02-01">
+    enum="Boolean" expires_after="2026-04-05">
   <owner>amoylan@chromium.org</owner>
   <owner>alanlxl@chromium.org</owner>
   <summary>
@@ -152,7 +152,7 @@
 
 <histogram name="MachineLearningService.LoadModelResult"
     enum="MachineLearningServiceLoadModelResultEvent"
-    expires_after="2025-11-01">
+    expires_after="2026-04-01">
   <owner>amoylan@chromium.org</owner>
   <owner>alanlxl@chromium.org</owner>
   <summary>
@@ -236,7 +236,7 @@
 </histogram>
 
 <histogram name="MachineLearningService.NumWorkerProcess" units="count"
-    expires_after="2025-11-09">
+    expires_after="2026-04-01">
   <owner>alanlxl@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <summary>
@@ -268,7 +268,7 @@
 
 <histogram name="MachineLearningService.ReapWorkerProcessErrno"
     enum="MachineLearningServiceReapWorkerProcessErrno"
-    expires_after="2025-11-01">
+    expires_after="2026-04-01">
   <owner>amoylan@chromium.org</owner>
   <owner>alanlxl@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cryptohome/histograms.xml b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
index 6e0fb96f..14c85fb 100644
--- a/tools/metrics/histograms/metadata/cryptohome/histograms.xml
+++ b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
@@ -388,7 +388,7 @@
 </histogram>
 
 <histogram name="Cryptohome.OOPMountOperationResult"
-    enum="CryptohomeOOPMountOperationResult" expires_after="2026-02-01">
+    enum="CryptohomeOOPMountOperationResult" expires_after="2026-04-05">
   <owner>sarthakkukreti@google.com</owner>
   <owner>cros-security-cryptohome+uma@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
index 155e0a5..dedf74b3 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -671,7 +671,7 @@
 </histogram>
 
 <histogram name="CustomTabs.PartialCustomTabType"
-    enum="CustomTabsPartialCustomTabType" expires_after="2026-02-01">
+    enum="CustomTabsPartialCustomTabType" expires_after="2026-04-05">
   <owner>kgrosu@google.com</owner>
   <owner>chrome-connective-tissue@google.com</owner>
   <summary>
@@ -876,7 +876,7 @@
 </histogram>
 
 <histogram name="CustomTabs.TimeSpentInEngagementEventIpc" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>nafisabedin@google.com</owner>
   <summary>
     The time spent in milliseconds processing IPCs on the UI thread related to
diff --git a/tools/metrics/histograms/metadata/enterprise/enums.xml b/tools/metrics/histograms/metadata/enterprise/enums.xml
index 34afb35..91ce8bc 100644
--- a/tools/metrics/histograms/metadata/enterprise/enums.xml
+++ b/tools/metrics/histograms/metadata/enterprise/enums.xml
@@ -111,6 +111,12 @@
   <int value="8" label="STORE_KEY_UNAVAILABLE_MANAGED"/>
 </enum>
 
+<enum name="DeviceSignalsHarmfulAppsStatus">
+  <int value="0" label="Success"/>
+  <int value="1" label="Timeout"/>
+  <int value="2" label="Failed"/>
+</enum>
+
 <enum name="DeviceSignalsParsingError">
   <int value="0" label="Hit max data size"/>
   <int value="1" label="Data malformed"/>
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml
index 960f6e2..e766212 100644
--- a/tools/metrics/histograms/metadata/enterprise/histograms.xml
+++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -574,7 +574,7 @@
 </histogram>
 
 <histogram name="Enterprise.CloudManagement.PolicyFetchingTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ftirelo@chromium.org</owner>
   <owner>zmin@chromium.org</owner>
   <summary>
@@ -605,7 +605,7 @@
 </histogram>
 
 <histogram name="Enterprise.CloudReportingRequestCount" units="requests"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>zmin@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
@@ -615,7 +615,7 @@
 </histogram>
 
 <histogram name="Enterprise.CloudReportingRequestSize" units="KB"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>zmin@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
@@ -625,7 +625,7 @@
 </histogram>
 
 <histogram name="Enterprise.CloudReportingResponse"
-    enum="EnterpriseCloudReportingResponse" expires_after="2026-02-01">
+    enum="EnterpriseCloudReportingResponse" expires_after="2026-04-05">
   <owner>zmin@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
@@ -880,7 +880,7 @@
 </histogram>
 
 <histogram name="Enterprise.DeviceSettings.UpdatedStatus2"
-    enum="DeviceSettingsStatus2" expires_after="2026-02-01">
+    enum="DeviceSettingsStatus2" expires_after="2026-04-05">
   <owner>rbock@google.com</owner>
   <owner>chromeos-commercial-remote-management@google.com</owner>
   <owner>managed-devices@google.com</owner>
@@ -1010,6 +1010,26 @@
   </token>
 </histogram>
 
+<histogram name="Enterprise.DeviceSignals.HarmfulApps.Count" units="count"
+    expires_after="2026-10-01">
+  <owner>xzonghan@chromium.org</owner>
+  <owner>cbe-device-trust-eng@google.com</owner>
+  <summary>
+    Recorded the number of potentialy harmful apps when SafetyNet correctly
+    collect this information.
+  </summary>
+</histogram>
+
+<histogram name="Enterprise.DeviceSignals.HarmfulApps.Result"
+    enum="DeviceSignalsHarmfulAppsStatus" expires_after="2026-10-01">
+  <owner>xzonghan@chromium.org</owner>
+  <owner>cbe-device-trust-eng@google.com</owner>
+  <summary>
+    Recorded the success status of SafetyNet API when collecting potentially
+    harmful apps.
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.DeviceSignals.UserPermission"
     enum="DeviceSignalsUserPermission" expires_after="2026-02-10">
   <owner>seblalancette@chromium.org</owner>
@@ -2560,7 +2580,7 @@
 
 <histogram name="Enterprise.MachineLevelUserCloudPolicyEnrollment.Result"
     enum="MachineLevelUserCloudPolicyEnrollmentResult"
-    expires_after="2025-12-01">
+    expires_after="2026-04-05">
   <owner>zmin@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>The result of machine level user cloud policy enrollment.</summary>
@@ -2858,7 +2878,7 @@
 </histogram>
 
 <histogram name="Enterprise.PolicyServiceInitTime{Scope}{PolicyCountSuffix}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>ftirelo@chromium.org</owner>
   <owner>zmin@chromium.org</owner>
   <summary>
@@ -3104,7 +3124,7 @@
 </histogram>
 
 <histogram name="Enterprise.ReportingEventUploadFailure"
-    enum="EnterpriseReportingEventType" expires_after="2026-02-01">
+    enum="EnterpriseReportingEventType" expires_after="2026-04-05">
   <owner>xanth@google.com</owner>
   <owner>alshawwa@chromium.org</owner>
   <owner>domfc@chromium.org</owner>
@@ -3116,7 +3136,7 @@
 </histogram>
 
 <histogram name="Enterprise.ReportingEventUploadSuccess"
-    enum="EnterpriseReportingEventType" expires_after="2026-02-01">
+    enum="EnterpriseReportingEventType" expires_after="2026-04-05">
   <owner>xanth@google.com</owner>
   <owner>alshawwa@chromium.org</owner>
   <owner>domfc@chromium.org</owner>
@@ -3200,7 +3220,7 @@
 </histogram>
 
 <histogram name="Enterprise.SkyVault.LocalStorage.Enabled"
-    enum="BooleanEnabled" expires_after="2026-02-01">
+    enum="BooleanEnabled" expires_after="2026-04-05">
   <owner>aidazolic@google.com</owner>
   <owner>src/chrome/browser/ash/policy/skyvault/OWNERS</owner>
   <summary>
@@ -3510,7 +3530,7 @@
 </histogram>
 
 <histogram name="Enterprise.StateDetermination.Enabled" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>sergiyb@google.com</owner>
   <owner>chromeos-commercial-remote-management@google.com</owner>
   <summary>Captures situations when state determination is enabled.</summary>
@@ -3539,7 +3559,7 @@
 </histogram>
 
 <histogram name="Enterprise.StateDetermination.OwnershipStatus"
-    enum="OwnershipStatus" expires_after="2026-02-01">
+    enum="OwnershipStatus" expires_after="2026-04-05">
   <owner>sergiyb@google.com</owner>
   <owner>chromeos-commercial-remote-management@google.com</owner>
   <summary>
@@ -3579,7 +3599,7 @@
 </histogram>
 
 <histogram name="Enterprise.StateDetermination.StateReturned" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>sergiyb@google.com</owner>
   <owner>chromeos-commercial-remote-management@google.com</owner>
   <summary>
@@ -3588,7 +3608,7 @@
 </histogram>
 
 <histogram name="Enterprise.StateDetermination.Status"
-    enum="StateDeterminationStatus" expires_after="2026-02-01">
+    enum="StateDeterminationStatus" expires_after="2026-04-05">
   <owner>sergiyb@google.com</owner>
   <owner>chromeos-commercial-remote-management@google.com</owner>
   <summary>
@@ -3607,14 +3627,14 @@
 </histogram>
 
 <histogram name="Enterprise.StateDetermination.TotalDuration" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>sergiyb@google.com</owner>
   <owner>chromeos-commercial-remote-management@google.com</owner>
   <summary>Tracks the total duration of state determination.</summary>
 </histogram>
 
 <histogram name="Enterprise.StateDetermination.TotalDurationByState.{State}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>sergiyb@google.com</owner>
   <owner>chromeos-commercial-remote-management@google.com</owner>
   <summary>
@@ -3626,7 +3646,7 @@
 
 <histogram
     name="Enterprise.StateDetermination.{RequestType}Request.DmStatusCode"
-    enum="EnterpriseDeviceManagementStatus" expires_after="2026-02-01">
+    enum="EnterpriseDeviceManagementStatus" expires_after="2026-04-05">
   <owner>sergiyb@chromium.org</owner>
   <owner>chromeos-commercial-remote-management@google.com</owner>
   <summary>
@@ -4026,7 +4046,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.IsEnterpriseUser" enum="BooleanEnabled"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>pastarmovj@chromium.org</owner>
   <owner>zmin@chromium.org</owner>
   <summary>
@@ -4037,7 +4057,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.IsFullyManaged2" enum="IsFullyManagedBoolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>twellington@google.com</owner>
   <owner>tedchcoc@chromium.org</owner>
   <summary>
@@ -4061,7 +4081,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.IsLocalMachine" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>zmin@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
@@ -4082,7 +4102,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.IsManaged2" enum="BooleanEnabled"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>zmin@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
@@ -4095,7 +4115,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.IsManagedOrEnterpriseDevice"
-    enum="BooleanEnabled" expires_after="2026-02-01">
+    enum="BooleanEnabled" expires_after="2026-04-05">
   <owner>pastarmovj@chromium.org</owner>
   <owner>zmin@chromium.org</owner>
   <summary>
@@ -4167,7 +4187,7 @@
 </histogram>
 
 <histogram name="EnterpriseCheck.WindowsProfileType" enum="WindowsProfileType"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>wfh@chromium.org</owner>
   <owner>chrome-platform-security-core@google.com</owner>
   <summary>
@@ -4177,7 +4197,7 @@
 </histogram>
 
 <histogram name="NetworkedProfile.Check" enum="NetworkedProfileCheck"
-    expires_after="2026-01-30">
+    expires_after="2026-04-05">
   <owner>pastarmovj@chromium.org</owner>
   <owner>grt@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml
index 3596919..d4cebe1 100644
--- a/tools/metrics/histograms/metadata/extensions/histograms.xml
+++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -276,7 +276,7 @@
 </histogram>
 
 <histogram name="Extensions.AppLaunch" enum="AppLaunch"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>benwells@chromium.org</owner>
   <owner>dominickn@chromium.org</owner>
   <owner>tapted@chromium.org</owner>
@@ -893,7 +893,7 @@
 </histogram>
 
 <histogram name="Extensions.DaysSinceInstall" units="days"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>psarouthakis@google.com</owner>
   <owner>anunoy@chromium.org</owner>
   <summary>
@@ -904,7 +904,7 @@
 </histogram>
 
 <histogram name="Extensions.DaysSinceLastUpdate" units="days"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>psarouthakis@google.com</owner>
   <owner>anunoy@chromium.org</owner>
   <summary>
@@ -1005,7 +1005,7 @@
 
 <histogram
     name="Extensions.DeclarativeNetRequest.EvaluateRequestTime.AllExtensions3"
-    units="microseconds" expires_after="2026-02-02">
+    units="microseconds" expires_after="2026-04-05">
   <owner>kelvinjiang@chromium.org</owner>
   <owner>src/extensions/OWNERS</owner>
   <summary>
@@ -1021,7 +1021,7 @@
 </histogram>
 
 <histogram name="Extensions.DeclarativeNetRequest.IndexAndPersistRulesTime"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>kelvinjiang@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -1047,7 +1047,7 @@
 
 <histogram
     name="Extensions.DeclarativeNetRequest.LoadRulesetExtensionVersionMatch.{LoadRulesetRequestSource}"
-    enum="BooleanSuccess" expires_after="2026-02-01">
+    enum="BooleanSuccess" expires_after="2026-04-05">
   <owner>kelvinjiang@chromium.org</owner>
   <owner>src/extensions/OWNERS</owner>
   <summary>
@@ -1237,7 +1237,7 @@
 </histogram>
 
 <histogram name="Extensions.DeclarativeNetRequest.ResponseHeaderRemoved"
-    enum="WebRequest.ResponseHeader" expires_after="2026-02-01">
+    enum="WebRequest.ResponseHeader" expires_after="2026-04-05">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>kelvinjiang@chromium.org</owner>
   <owner>src/extensions/OWNERS</owner>
@@ -1986,7 +1986,7 @@
 </histogram>
 
 <histogram name="Extensions.ExternalItemState2" enum="ExternalItemState"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>anunoy@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -2266,7 +2266,7 @@
 </histogram>
 
 <histogram name="Extensions.ForceInstalledManagementAuthorityTrustworthiness"
-    enum="ManagementAuthorityTrustworthiness" expires_after="2026-02-01">
+    enum="ManagementAuthorityTrustworthiness" expires_after="2026-04-05">
   <owner>richche@google.com</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -2679,7 +2679,7 @@
 </histogram>
 
 <histogram name="Extensions.Functions.SucceededTotalExecutionTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -3037,7 +3037,7 @@
 </histogram>
 
 <histogram name="Extensions.InstallFrictionDialogAction"
-    enum="ExtensionInstallFrictionDialogAction" expires_after="2026-02-01">
+    enum="ExtensionInstallFrictionDialogAction" expires_after="2026-04-05">
   <owner>jeffcyr@google.com</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -3685,7 +3685,7 @@
 </histogram>
 
 <histogram name="Extensions.NewTabPageOverrides2" units="units"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>kelvinjiang@chromium.org</owner>
   <summary>
@@ -3731,7 +3731,7 @@
 </histogram>
 
 <histogram name="Extensions.NonWebStoreNewTabPageOverrides2" units="units"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>anunoy@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -4668,7 +4668,7 @@
 
 <histogram name="Extensions.WebRequest.ProxyDecisionDetailsForExtension"
     enum="WebRequest.ProxyDecisionDetailsForExtension"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>toyoshim@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -5048,7 +5048,7 @@
 </histogram>
 
 <histogram name="GuestView.GuestViewCreated" enum="GuestViewType"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>mcnee@chromium.org</owner>
   <owner>wjmaclean@chromium.org</owner>
   <owner>src/components/guest_view/OWNERS</owner>
diff --git a/tools/metrics/histograms/metadata/families/histograms.xml b/tools/metrics/histograms/metadata/families/histograms.xml
index b4ed542..d9a7219 100644
--- a/tools/metrics/histograms/metadata/families/histograms.xml
+++ b/tools/metrics/histograms/metadata/families/histograms.xml
@@ -882,7 +882,7 @@
 </histogram>
 
 <histogram name="SupervisedUsers.ClassifyUrlThrottle.FinalStatus"
-    enum="ClassifyUrlThrottleFinalStatus" expires_after="2026-02-01">
+    enum="ClassifyUrlThrottleFinalStatus" expires_after="2026-04-05">
   <owner>tju@google.com</owner>
   <owner>chrome-kids-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
index 7de1ae2e..ead850a 100644
--- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
+++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -729,7 +729,7 @@
 </variants>
 
 <histogram name="InProductHelp.Config.ParsingEvent" enum="ConfigParsingEvent"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>nyquist@chromium.org</owner>
   <summary>
     Records if in-product help configuration is parsed correctly, and the
@@ -776,7 +776,7 @@
 </histogram>
 
 <histogram name="InProductHelp.DismissalReason.iOS"
-    enum="InProductHelpDismissalReason" expires_after="2026-02-01">
+    enum="InProductHelpDismissalReason" expires_after="2026-04-05">
   <owner>lpromero@google.com</owner>
   <owner>gambard@chromium.org</owner>
   <summary>
@@ -785,7 +785,7 @@
 </histogram>
 
 <histogram name="InProductHelp.EventStorageMigration.Status"
-    enum="InProductHelpEventStorageMigrationStatus" expires_after="2026-02-01">
+    enum="InProductHelpEventStorageMigrationStatus" expires_after="2026-04-05">
   <owner>cheickcisse@google.com</owner>
   <owner>shaktisahu@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/gcm/histograms.xml b/tools/metrics/histograms/metadata/gcm/histograms.xml
index f86df7201..48fadb1 100644
--- a/tools/metrics/histograms/metadata/gcm/histograms.xml
+++ b/tools/metrics/histograms/metadata/gcm/histograms.xml
@@ -92,7 +92,7 @@
 </histogram>
 
 <histogram name="GCM.DelayedTaskControlledReadyTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>rushans@google.com</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -168,7 +168,7 @@
 </histogram>
 
 <histogram name="GCM.RegistrationRequestStatus"
-    enum="GCMRegistrationRequestStatus" expires_after="2026-02-01">
+    enum="GCMRegistrationRequestStatus" expires_after="2026-04-05">
   <owner>peter@chromium.org</owner>
   <summary>
     Status code of the outcome of a GCM registration request. The Unknown error
diff --git a/tools/metrics/histograms/metadata/geolocation/histograms.xml b/tools/metrics/histograms/metadata/geolocation/histograms.xml
index d972ca0..2990372 100644
--- a/tools/metrics/histograms/metadata/geolocation/histograms.xml
+++ b/tools/metrics/histograms/metadata/geolocation/histograms.xml
@@ -310,7 +310,7 @@
 </histogram>
 
 <histogram name="Geolocation.NetworkLocationRequest.Result"
-    enum="NetworkLocationRequestResult" expires_after="2026-02-01">
+    enum="NetworkLocationRequestResult" expires_after="2026-04-05">
   <owner>mattreynolds@chromium.org</owner>
   <owner>device-dev@chromium.org</owner>
   <summary>
@@ -404,7 +404,7 @@
 
 <histogram
     name="Geolocation.SystemGeolocationSourceWin.PermissionStatusChangedAfterPrompt"
-    enum="LocationSystemPermissionStatus" expires_after="2026-02-01">
+    enum="LocationSystemPermissionStatus" expires_after="2026-04-05">
   <owner>mattreynolds@chromium.org</owner>
   <owner>device-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/glic/histograms.xml b/tools/metrics/histograms/metadata/glic/histograms.xml
index e4e543e..5bf8cac 100644
--- a/tools/metrics/histograms/metadata/glic/histograms.xml
+++ b/tools/metrics/histograms/metadata/glic/histograms.xml
@@ -467,7 +467,7 @@
 </histogram>
 
 <histogram name="Glic.Host.WebView.AutoPlay" enum="WebViewAutoPlayProgress"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>carlosk@chromium.org</owner>
   <owner>dewittj@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/gpu/histograms.xml b/tools/metrics/histograms/metadata/gpu/histograms.xml
index 6ea0aa4..a6660ee 100644
--- a/tools/metrics/histograms/metadata/gpu/histograms.xml
+++ b/tools/metrics/histograms/metadata/gpu/histograms.xml
@@ -309,7 +309,7 @@
 </histogram>
 
 <histogram name="GPU.Android.HasEGLDupNativeFenceFunction" enum="Boolean"
-    expires_after="M145">
+    expires_after="2026-04-05">
   <owner>boliu@chromium.org</owner>
   <owner>vasilyt@chromium.org</owner>
   <owner>chrome-gpu-metric-alerts@google.com</owner>
@@ -638,7 +638,7 @@
 </histogram>
 
 <histogram name="GPU.DirectComposition.DCLayer.YUVOverlayCount"
-    units="overlays" expires_after="2026-02-01">
+    units="overlays" expires_after="2026-04-05">
   <owner>magchen@chromium.org</owner>
   <owner>zmo@chromium.org</owner>
   <summary>
@@ -1397,7 +1397,7 @@
 </histogram>
 
 <histogram name="GPU.SharedImage.SharedImageFormat" enum="SharedImageFormatUMA"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hitawala@chromium.org</owner>
   <owner>chrome-gpu-metric-alerts@chromium.org</owner>
   <summary>
@@ -2003,7 +2003,7 @@
 </histogram>
 
 <histogram name="Viz.DisplayCompositor.RootDamageRect.Overlay"
-    enum="BooleanOverlayDamageRect" expires_after="2026-02-01">
+    enum="BooleanOverlayDamageRect" expires_after="2026-04-05">
   <owner>magchen@chromium.org</owner>
   <owner>zmo@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml
index 35d92853..b80b1f4d 100644
--- a/tools/metrics/histograms/metadata/history/histograms.xml
+++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -2661,7 +2661,7 @@
 </histogram>
 
 <histogram name="History.Is4XXOr5XXStatusCode" enum="Boolean"
-    expires_after="2026-01-31">
+    expires_after="2026-04-05">
   <owner>kyraseevers@chromium.org</owner>
   <owner>janiceliu@chromium.org</owner>
   <summary>
@@ -2723,7 +2723,7 @@
 </histogram>
 
 <histogram name="History.MostVisitedTilesVisualDeduplication" units="tiles"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hitarthkothari@google.com</owner>
   <owner>ckitagawa@chromium.org</owner>
   <summary>
@@ -2740,7 +2740,7 @@
 </histogram>
 
 <histogram name="History.QueryAppDuration" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>jinsukkim@google.com</owner>
   <owner>chrome-connective-tissue-team@google.com</owner>
   <component>1456716</component>
@@ -3039,7 +3039,7 @@
 </histogram>
 
 <histogram name="History.WeeklyURLCount" units="URLs"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>mpearson@chromium.org</owner>
   <owner>sky@chromium.org</owner>
   <component>1456716</component>
diff --git a/tools/metrics/histograms/metadata/image/histograms.xml b/tools/metrics/histograms/metadata/image/histograms.xml
index b3cea20f..0c1e60b 100644
--- a/tools/metrics/histograms/metadata/image/histograms.xml
+++ b/tools/metrics/histograms/metadata/image/histograms.xml
@@ -458,7 +458,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.CacheMetadataCount{ImageFetcherCacheStrategy}"
-    units="records" expires_after="2025-10-12">
+    units="records" expires_after="2026-03-10">
   <owner>wylieb@google.com</owner>
   <owner>skym@chromium.org</owner>
   <summary>
@@ -470,7 +470,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.CacheSize{ImageFetcherCacheStrategy}" units="KB"
-    expires_after="2025-10-12">
+    expires_after="2026-03-10">
   <owner>wylieb@google.com</owner>
   <owner>skym@chromium.org</owner>
   <summary>
@@ -481,7 +481,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.Events{ImageFetcherClients}"
-    enum="ImageFetcherEvent" expires_after="2026-02-22">
+    enum="ImageFetcherEvent" expires_after="2026-03-10">
   <owner>wylieb@google.com</owner>
   <owner>skym@chromium.org</owner>
   <summary>
@@ -495,7 +495,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.ImageLoadFromCacheTimeJava{ImageFetcherClients}"
-    units="ms" expires_after="2025-10-12">
+    units="ms" expires_after="2026-03-10">
   <owner>wylieb@google.com</owner>
   <owner>skym@chromium.org</owner>
   <summary>
@@ -521,7 +521,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.ImageLoadFromNativeTimeJava{ImageFetcherClients}"
-    units="ms" expires_after="2025-10-12">
+    units="ms" expires_after="2026-03-10">
   <owner>wylieb@google.com</owner>
   <owner>skym@chromium.org</owner>
   <summary>
@@ -535,7 +535,7 @@
 
 <histogram
     name="ImageFetcher.ImageLoadFromNetworkAfterCacheHit{ImageFetcherClients}"
-    units="ms" expires_after="2026-02-22">
+    units="ms" expires_after="2026-03-10">
   <owner>wylieb@google.com</owner>
   <owner>skym@chromium.org</owner>
   <summary>
@@ -548,7 +548,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.ImageLoadFromNetworkTime{ImageFetcherClients}"
-    units="ms" expires_after="2026-02-22">
+    units="ms" expires_after="2026-03-10">
   <owner>wylieb@google.com</owner>
   <owner>skym@chromium.org</owner>
   <summary>
@@ -561,7 +561,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.LoadImageMetadata" units="ms"
-    expires_after="2025-10-12">
+    expires_after="2026-03-10">
   <owner>wylieb@google.com</owner>
   <owner>skym@chromium.org</owner>
   <summary>
@@ -571,7 +571,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.RequestStatusCode{ImageFetcherClients}"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2026-02-22">
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2026-03-10">
   <owner>wylieb@google.com</owner>
   <owner>skym@chromium.org</owner>
   <summary>
@@ -585,7 +585,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.TimeSinceLastCacheLRUEviction" units="ms"
-    expires_after="2025-10-12">
+    expires_after="2026-03-10">
   <owner>wylieb@google.com</owner>
   <owner>skym@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/input/histograms.xml b/tools/metrics/histograms/metadata/input/histograms.xml
index 31a78369..b231632 100644
--- a/tools/metrics/histograms/metadata/input/histograms.xml
+++ b/tools/metrics/histograms/metadata/input/histograms.xml
@@ -1152,7 +1152,7 @@
 </histogram>
 
 <histogram name="InputMethod.KeyEventLatency" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>shend@chromium.org</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>Time taken by the engine to handle a key event.</summary>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index d3ac912..20ca1d9 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -528,7 +528,7 @@
 
 <histogram name="IOS.ContextualPanel.Entrypoint.{EntrypointType}"
     enum="IOSContextualPanelEntrypointInteractionType"
-    expires_after="2026-01-30">
+    expires_after="2026-04-05">
   <owner>rkgibson@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>
@@ -548,7 +548,7 @@
 
 <histogram
     name="IOS.ContextualPanel.Entrypoint.{EntrypointType}.UptimeBeforeTap"
-    units="ms" expires_after="2026-01-30">
+    units="ms" expires_after="2026-04-05">
   <owner>rkgibson@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>
@@ -568,7 +568,7 @@
 <histogram
     name="IOS.ContextualPanel.Entrypoint.{EntrypointType}.{ContextualPanelItemType}"
     enum="IOSContextualPanelEntrypointInteractionType"
-    expires_after="2026-01-30">
+    expires_after="2026-04-05">
   <owner>rkgibson@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>
@@ -619,7 +619,7 @@
 </histogram>
 
 <histogram name="IOS.ContextualPanel.EntrypointTapped"
-    enum="IOSContextualPanelItemType" expires_after="2026-01-30">
+    enum="IOSContextualPanelItemType" expires_after="2026-04-05">
   <owner>rkgibson@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>
@@ -643,7 +643,7 @@
 </histogram>
 
 <histogram name="IOS.ContextualPanel.InfoBlockUptime.{ContextualPanelItemType}"
-    units="ms" expires_after="2026-01-30">
+    units="ms" expires_after="2026-04-05">
   <owner>rkgibson@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>
@@ -1086,7 +1086,7 @@
 </histogram>
 
 <histogram name="IOS.DefaultBrowserBannerPromo.ManuallyDismissed" units="count"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>rkgibson@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>
@@ -1099,7 +1099,7 @@
 
 <histogram name="IOS.DefaultBrowserBannerPromo.PromoSessionEnded"
     enum="IOSDefaultBrowserBannerPromoPromoSessionEndedReason"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>rkgibson@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>
@@ -1109,7 +1109,7 @@
 </histogram>
 
 <histogram name="IOS.DefaultBrowserBannerPromo.Shown" units="count"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>rkgibson@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>
@@ -1121,7 +1121,7 @@
 </histogram>
 
 <histogram name="IOS.DefaultBrowserBannerPromo.Tapped" units="count"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>rkgibson@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>
@@ -2094,7 +2094,7 @@
 </histogram>
 
 <histogram name="IOS.Frame.FirstContentfulPaint.MainFrame" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>danieltwhite@chromium.org</owner>
   <owner>bling-fundamentals@google.com</owner>
   <summary>
@@ -2110,7 +2110,7 @@
 </histogram>
 
 <histogram name="IOS.Frame.FirstContentfulPaint.SubFrame" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>danieltwhite@chromium.org</owner>
   <owner>bling-fundamentals@google.com</owner>
   <summary>
@@ -2198,7 +2198,7 @@
 </histogram>
 
 <histogram name="IOS.Fullscreen.State" enum="CrFullscreenState"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>joemerramos@chromium.org</owner>
   <owner>bling-fundamentals@google.com</owner>
   <summary>
@@ -2234,7 +2234,7 @@
 </histogram>
 
 <histogram name="IOS.Gemini.Eligibility" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>sebsg@chromium.org</owner>
   <owner>bling-alchemy@google.com</owner>
   <summary>
@@ -2244,7 +2244,7 @@
 </histogram>
 
 <histogram name="IOS.Gemini.EntryPoint" enum="IOSGeminiEntryPoint"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>sebsg@chromium.org</owner>
   <owner>bling-alchemy@google.com</owner>
   <summary>
@@ -2254,7 +2254,7 @@
 </histogram>
 
 <histogram name="IOS.Gemini.FirstPrompt.SubmissionMethod"
-    enum="IOSGeminiFirstPromptSubmissionMethod" expires_after="2026-02-01">
+    enum="IOSGeminiFirstPromptSubmissionMethod" expires_after="2026-04-05">
   <owner>kanouche@google.com</owner>
   <owner>bling-alchemy@google.com</owner>
   <summary>
@@ -2275,7 +2275,7 @@
 </histogram>
 
 <histogram name="IOS.Gemini.FRE.EntryPoint" enum="IOSGeminiEntryPoint"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>sebsg@chromium.org</owner>
   <owner>bling-alchemy@google.com</owner>
   <summary>
@@ -2303,7 +2303,7 @@
 </histogram>
 
 <histogram name="IOS.Gemini.Response.Latency.WithContext" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>kanouche@google.com</owner>
   <owner>bling-alchemy@google.com</owner>
   <summary>
@@ -2313,7 +2313,7 @@
 </histogram>
 
 <histogram name="IOS.Gemini.Response.Latency.WithoutContext" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>kanouche@google.com</owner>
   <owner>bling-alchemy@google.com</owner>
   <summary>
@@ -2323,7 +2323,7 @@
 </histogram>
 
 <histogram name="IOS.Gemini.Session.FirstPrompt" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>kanouche@google.com</owner>
   <owner>bling-alchemy@google.com</owner>
   <summary>
@@ -2333,7 +2333,7 @@
 </histogram>
 
 <histogram name="IOS.Gemini.Session.PromptCount" units="count"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>kanouche@google.com</owner>
   <owner>bling-alchemy@google.com</owner>
   <summary>
@@ -3098,7 +3098,7 @@
 </histogram>
 
 <histogram name="IOS.MagicStack.Module.Click.AppBundlePromo" units="index"
-    expires_after="2025-12-02">
+    expires_after="2026-04-05">
   <owner>ericekey@google.com</owner>
   <owner>bling-pandamonium@google.com</owner>
   <summary>
@@ -3300,7 +3300,7 @@
 </histogram>
 
 <histogram name="IOS.MagicStack.TabResumption.{Type}.Impression" units="index"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>davidjm@chromium.org</owner>
   <owner>thegreenfrog@google.com</owner>
   <owner>gloriafang@google.com</owner>
@@ -3952,7 +3952,7 @@
 </histogram>
 
 <histogram name="IOS.NTP.Click" enum="IOSHomeActionType"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>thegreenfrog@chromium.org</owner>
   <owner>bling-team@google.com</owner>
   <summary>
@@ -4009,7 +4009,7 @@
 </histogram>
 
 <histogram name="IOS.NTP.OverscrollAction" enum="IOSNTPOverscrollAction"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>guiperez@google.com</owner>
   <owner>bling-team@google.com</owner>
   <summary>
@@ -4726,7 +4726,7 @@
 </histogram>
 
 <histogram name="IOS.Process.ActivePrewarm" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>justincohen@google.com</owner>
   <owner>olivierrobin@google.com</owner>
   <summary>
@@ -5271,7 +5271,7 @@
 </histogram>
 
 <histogram name="IOS.Reauth.Password.Autofill" enum="ReauthenticationEvent"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>sugoi@chromium.org</owner>
   <owner>bling-transactions@google.com</owner>
   <summary>
@@ -6102,21 +6102,21 @@
 </histogram>
 
 <histogram name="IOS.Snapshots.RetrieveColorSnapshotTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>asamidoi@chromium.org</owner>
   <owner>bling-kiwi-pod@google.com</owner>
   <summary>Records the time to retrieve a color snapshot.</summary>
 </histogram>
 
 <histogram name="IOS.Snapshots.RetrieveGreySnapshotTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>asamidoi@chromium.org</owner>
   <owner>bling-kiwi-pod@google.com</owner>
   <summary>Records the time to retrieve a grey snapshot.</summary>
 </histogram>
 
 <histogram name="IOS.Snapshots.UpdateSnapshotTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>asamidoi@chromium.org</owner>
   <owner>bling-kiwi-pod@google.com</owner>
   <summary>Records the time to update a snapshot.</summary>
@@ -6251,7 +6251,7 @@
 </histogram>
 
 <histogram name="IOS.Start.Click" enum="IOSHomeActionType"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>thegreenfrog@chromium.org</owner>
   <owner>bling-team@google.com</owner>
   <summary>
@@ -6797,7 +6797,7 @@
 </histogram>
 
 <histogram name="IOS.WhatsNew.ItemsClickedCount" units="count"
-    expires_after="2026-01-30">
+    expires_after="2026-04-05">
   <owner>cheickcisse@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>The number of clicked What's New items per impression</summary>
@@ -6814,7 +6814,7 @@
 </histogram>
 
 <histogram name="IOS.WhatsNew.PrimaryActionTapped" enum="WhatsNewType"
-    expires_after="2026-01-30">
+    expires_after="2026-04-05">
   <owner>cheickcisse@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>
@@ -6824,7 +6824,7 @@
 </histogram>
 
 <histogram name="IOS.WhatsNew.Shown" enum="WhatsNewType"
-    expires_after="2026-01-30">
+    expires_after="2026-04-05">
   <owner>cheickcisse@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>
@@ -6834,7 +6834,7 @@
 </histogram>
 
 <histogram name="IOS.WhatsNew.TableViewDidScroll" enum="Boolean"
-    expires_after="2026-01-30">
+    expires_after="2026-04-05">
   <owner>cheickcisse@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>
@@ -6842,7 +6842,7 @@
   </summary>
 </histogram>
 
-<histogram name="IOS.WhatsNew.TimeSpent" units="ms" expires_after="2026-01-30">
+<histogram name="IOS.WhatsNew.TimeSpent" units="ms" expires_after="2026-04-05">
   <owner>cheickcisse@google.com</owner>
   <owner>bling-mony-pod@google.com</owner>
   <summary>Logs the time a user spent on What's New.</summary>
diff --git a/tools/metrics/histograms/metadata/ip_protection/histograms.xml b/tools/metrics/histograms/metadata/ip_protection/histograms.xml
index 7a04b6d..08e37a3 100644
--- a/tools/metrics/histograms/metadata/ip_protection/histograms.xml
+++ b/tools/metrics/histograms/metadata/ip_protection/histograms.xml
@@ -619,7 +619,7 @@
 </histogram>
 
 <histogram name="NetworkService.IpProtection.TokenDemandDuringBatchGeneration"
-    units="tokens" expires_after="2026-01-07">
+    units="tokens" expires_after="2026-04-05">
   <owner>jtoohill@google.com</owner>
   <owner>awillia@chromium.org</owner>
   <owner>src/components/ip_protection/OWNERS</owner>
diff --git a/tools/metrics/histograms/metadata/language/histograms.xml b/tools/metrics/histograms/metadata/language/histograms.xml
index af0e0f1..57e5ddc 100644
--- a/tools/metrics/histograms/metadata/language/histograms.xml
+++ b/tools/metrics/histograms/metadata/language/histograms.xml
@@ -36,7 +36,7 @@
 </histogram>
 
 <histogram name="LanguageDetection.TFLiteModel.ClassifyText.Duration"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>fergal@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -65,7 +65,7 @@
 </histogram>
 
 <histogram name="LanguageDetection.TFLiteModel.ClassifyText.Size"
-    units="characters" expires_after="2026-02-01">
+    units="characters" expires_after="2026-04-05">
   <owner>fergal@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -105,7 +105,7 @@
 </histogram>
 
 <histogram name="LanguageDetection.TFLiteModel.DetectPageLanguage.Duration"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>fergal@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/lens/histograms.xml b/tools/metrics/histograms/metadata/lens/histograms.xml
index d9ca292..830c94e 100644
--- a/tools/metrics/histograms/metadata/lens/histograms.xml
+++ b/tools/metrics/histograms/metadata/lens/histograms.xml
@@ -790,7 +790,7 @@
 </histogram>
 
 <histogram name="Lens.Overlay.SidePanelResultStatus"
-    enum="SidePanelResultStatus" expires_after="2026-02-01">
+    enum="SidePanelResultStatus" expires_after="2026-04-05">
   <owner>juanmojica@google.com</owner>
   <owner>lens-chrome@google.com</owner>
   <summary>
@@ -801,7 +801,7 @@
 </histogram>
 
 <histogram name="Lens.Overlay.TextDirectiveResult"
-    enum="LensOverlayTextDirectiveResult" expires_after="2026-01-31">
+    enum="LensOverlayTextDirectiveResult" expires_after="2026-04-05">
   <owner>juanmojica@google.com</owner>
   <owner>lens-chrome@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/login/histograms.xml b/tools/metrics/histograms/metadata/login/histograms.xml
index f27758d..a19aaf7c7 100644
--- a/tools/metrics/histograms/metadata/login/histograms.xml
+++ b/tools/metrics/histograms/metadata/login/histograms.xml
@@ -212,7 +212,7 @@
 </histogram>
 
 <histogram name="Login.OfflineLoginWithHiddenUserPods"
-    enum="ChromeOSHiddenUserPodsOfflineLogin" expires_after="2026-02-01">
+    enum="ChromeOSHiddenUserPodsOfflineLogin" expires_after="2026-04-05">
   <owner>mslus@chromium.org</owner>
   <owner>chromeos-commercial-identity@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/media/enums.xml b/tools/metrics/histograms/metadata/media/enums.xml
index b1df99b..9d9ad8c 100644
--- a/tools/metrics/histograms/metadata/media/enums.xml
+++ b/tools/metrics/histograms/metadata/media/enums.xml
@@ -573,6 +573,10 @@
   <int value="17" label="LegacyDesktopCaptureDeviceTypeScreen"/>
   <int value="18" label="LegacyDesktopCaptureDeviceTypeWindow"/>
   <int value="19" label="LegacyDesktopCaptureDeviceTypeWebContents"/>
+  <int value="20" label="NativeMacOSPickerCaptureDeviceTypeNone"/>
+  <int value="21" label="NativeMacOSPickerCaptureDeviceTypeScreen"/>
+  <int value="22" label="NativeMacOSPickerCaptureDeviceTypeWindow"/>
+  <int value="23" label="NativeMacOSPickerCaptureDeviceTypeWebContents"/>
 </enum>
 
 <enum name="DeviceEnumerationResult">
@@ -1406,6 +1410,17 @@
   <int value="12" label="Exit picture in picture"/>
 </enum>
 
+<!-- LINT.IfChange(MediaLauncherActivityEnabledReason) -->
+
+<enum name="MediaLauncherActivityEnabledReason">
+  <int value="0" label="Low end device"/>
+  <int value="1" label="Enterprise"/>
+  <int value="2" label="Both"/>
+  <int value="3" label="Neither"/>
+</enum>
+
+<!-- LINT.ThenChange(//chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java:MediaLauncherActivityEnabledReason) -->
+
 <enum name="MediaLoadType">
   <int value="0" label="URL"/>
   <int value="1" label="MediaSource"/>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index d328150..3b1f95a3 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -229,7 +229,7 @@
 </histogram>
 
 <histogram name="Cast.Sender.RemotePlayback.InitiationLocation"
-    enum="RemotePlaybackInitiationLocation" expires_after="2026-02-01">
+    enum="RemotePlaybackInitiationLocation" expires_after="2026-04-05">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -768,7 +768,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.LowLatencyCallbackError"
-    enum="BooleanError" expires_after="2026-02-01">
+    enum="BooleanError" expires_after="2026-04-05">
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <owner>webrtc-audio-uma@google.com</owner>
@@ -1025,7 +1025,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.Win.Open" enum="AudioStreamOpenResult"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>gudiou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <owner>fhernqvist@google.com</owner>
@@ -1109,7 +1109,7 @@
 </histogram>
 
 <histogram name="Media.Audio.CrasUnifiedStreamOpenSuccess"
-    enum="CrasUnifiedStreamOpenSuccess" expires_after="2026-02-01">
+    enum="CrasUnifiedStreamOpenSuccess" expires_after="2026-04-05">
   <owner>peah@chromium.org</owner>
   <owner>hychao@chromium.org</owner>
   <owner>olka@chromium.org</owner>
@@ -1469,7 +1469,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Processing.CaptureDelayMs" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>saza@google.com</owner>
   <owner>olka@chromium.org</owner>
   <owner>webrtc-audio-uma@google.com</owner>
@@ -1517,7 +1517,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Processing.TotalDelayMs" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>saza@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <owner>webrtc-audio-uma@google.com</owner>
@@ -1645,7 +1645,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Render.OutputDeviceStatus"
-    enum="OutputDeviceStatus" expires_after="2026-02-01">
+    enum="OutputDeviceStatus" expires_after="2026-04-05">
   <owner>dalecurtis@chromium.org</owner>
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
@@ -2233,7 +2233,7 @@
 </histogram>
 
 <histogram name="Media.AudioRendererImpl.SinkStatus" enum="OutputDeviceStatus"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>olka@chromium.org</owner>
   <owner>fhernqvist@google.com</owner>
   <owner>webrtc-audio-uma@google.com</owner>
@@ -2885,7 +2885,7 @@
 </histogram>
 
 <histogram name="Media.DocumentPictureInPicture.RequestedInitialHeight"
-    units="px" expires_after="2026-02-01">
+    units="px" expires_after="2026-04-05">
   <owner>steimel@chromium.org</owner>
   <owner>liberato@chromium.org</owner>
   <owner>media-dev-uma@chromium.org</owner>
@@ -2935,7 +2935,7 @@
 </histogram>
 
 <histogram name="Media.DocumentPictureInPicture.RequestedSizeToScreenRatio"
-    units="%" expires_after="2026-02-01">
+    units="%" expires_after="2026-04-05">
   <owner>steimel@chromium.org</owner>
   <owner>liberato@chromium.org</owner>
   <owner>media-dev-uma@chromium.org</owner>
@@ -3659,7 +3659,7 @@
 
 <histogram
     name="Media.EME.MediaFoundationService.ErrorNotAfterPowerOrDisplayChange2"
-    enum="Hresult" expires_after="2026-01-15">
+    enum="Hresult" expires_after="2026-04-05">
   <owner>xhwang@chromium.org</owner>
   <owner>media-dev-uma@chromium.org</owner>
   <summary>
@@ -3669,7 +3669,7 @@
 </histogram>
 
 <histogram name="Media.EME.MediaFoundationService.HardwareContextReset"
-    enum="BooleanAfterPowerOrDisplayChange" expires_after="2026-01-15">
+    enum="BooleanAfterPowerOrDisplayChange" expires_after="2026-04-05">
   <owner>xhwang@chromium.org</owner>
   <owner>media-dev-uma@chromium.org</owner>
   <summary>
@@ -4102,7 +4102,7 @@
 </histogram>
 
 <histogram name="Media.FrameInfo.GuessedInitialCodedSizeSuccess"
-    enum="BooleanSuccess" expires_after="2026-02-01">
+    enum="BooleanSuccess" expires_after="2026-04-05">
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev-uma@chromium.org</owner>
   <summary>
@@ -4645,7 +4645,7 @@
 </histogram>
 
 <histogram name="Media.LowLatencyAudioCaptureStartupSuccess"
-    enum="AudioCaptureStartupResult" expires_after="2026-02-01">
+    enum="AudioCaptureStartupResult" expires_after="2026-04-05">
   <owner>armax@chromium.org</owner>
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
@@ -4794,7 +4794,7 @@
 </histogram>
 
 <histogram name="Media.MediaDevices.GetDisplayMedia.Result2"
-    enum="UserMediaRequestResult" expires_after="2026-02-01">
+    enum="UserMediaRequestResult" expires_after="2026-04-05">
   <owner>toprice@chromium.org</owner>
   <owner>agpalak@chromium.org</owner>
   <owner>video-cmi-apis@google.com</owner>
@@ -5018,6 +5018,16 @@
   </summary>
 </histogram>
 
+<histogram name="Media.MediaLauncherActivityStarted"
+    enum="MediaLauncherActivityEnabledReason" expires_after="2026-06-01">
+  <owner>steimel@chromium.org</owner>
+  <owner>media-dev-uma@chromium.org</owner>
+  <summary>
+    Recorded when the MediaLauncherActivity starts up. Records the reaosn that
+    MediaLauncherActivity is enabled.
+  </summary>
+</histogram>
+
 <histogram name="Media.MediaPlaybackWhileNotVisible.InterruptionType"
     enum="MediaPlaybackInterruptionType" expires_after="2026-02-12">
   <owner>dalecurtis@chromium.org</owner>
@@ -5088,7 +5098,7 @@
 
 <histogram
     name="Media.MediaVideoVisibilityTracker.ComputeOcclusion.ComputeOccludingArea.TotalDuration"
-    units="microseconds" expires_after="2026-02-01">
+    units="microseconds" expires_after="2026-04-05">
   <owner>bkeen@google.com</owner>
   <owner>media-dev-uma@chromium.org</owner>
   <summary>
@@ -5179,7 +5189,7 @@
 
 <histogram
     name="Media.MediaVideoVisibilityTracker.OccludingRectsCount.{BucketSizeIncreases}Histogram.TotalCount"
-    units="rects" expires_after="2026-02-01">
+    units="rects" expires_after="2026-04-05">
   <owner>bkeen@google.com</owner>
   <owner>media-dev-uma@chromium.org</owner>
   <summary>
@@ -5217,7 +5227,7 @@
 
 <histogram
     name="Media.MediaVideoVisibilityTracker.{NodesCount}.{BucketSizeIncreases}Histogram.TotalCount"
-    units="nodes" expires_after="2026-02-01">
+    units="nodes" expires_after="2026-04-05">
   <owner>bkeen@google.com</owner>
   <owner>media-dev-uma@chromium.org</owner>
   <summary>
@@ -5250,7 +5260,7 @@
 
 <histogram
     name="Media.MediaVideoVisibilityTracker.{PercentageCategory}.Percentage"
-    units="%" expires_after="2026-02-01">
+    units="%" expires_after="2026-04-05">
   <owner>bkeen@google.com</owner>
   <owner>media-dev-uma@chromium.org</owner>
   <summary>
@@ -5576,7 +5586,7 @@
 </histogram>
 
 <histogram name="Media.OutputStreamDuration" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <owner>webrtc-audio-uma@google.com</owner>
@@ -6944,7 +6954,7 @@
 </histogram>
 
 <histogram name="Media.VideoCapture.DelayUntilFirstFrame" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>armax@chromium.org</owner>
   <owner>video-cmi-mpp@google.com</owner>
   <summary>
@@ -7218,7 +7228,7 @@
 </histogram>
 
 <histogram name="Media.VideoCapture.StartOutcome"
-    enum="VideoCaptureStartOutcome" expires_after="2026-02-01">
+    enum="VideoCaptureStartOutcome" expires_after="2026-04-05">
   <owner>toprice@chromium.org</owner>
   <owner>guidou@chromium.org</owner>
   <summary>
@@ -7672,7 +7682,7 @@
 </histogram>
 
 <histogram name="Media.VideoResourceUpdater.FrameFormat"
-    enum="VideoPixelFormatUnion" expires_after="2026-02-01">
+    enum="VideoPixelFormatUnion" expires_after="2026-04-05">
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -8042,7 +8052,7 @@
 
 <histogram
     name="MediaPreviews.UI.DeviceSelection.Permissions.Camera.NumDevices"
-    units="devices" expires_after="2026-02-01">
+    units="devices" expires_after="2026-04-05">
   <owner>bryantchandler@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -8054,7 +8064,7 @@
 </histogram>
 
 <histogram name="MediaPreviews.UI.DeviceSelection.Permissions.Mic.NumDevices"
-    units="devices" expires_after="2026-02-01">
+    units="devices" expires_after="2026-04-05">
   <owner>bryantchandler@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -8066,7 +8076,7 @@
 </histogram>
 
 <histogram name="MediaPreviews.UI.DeviceSelection.{UiLocation}.{Type}.Action"
-    enum="MediaPreviewDeviceSelectionUserAction" expires_after="2026-02-01">
+    enum="MediaPreviewDeviceSelectionUserAction" expires_after="2026-04-05">
   <owner>ahmedmoussa@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -8137,7 +8147,7 @@
 </histogram>
 
 <histogram name="MediaPreviews.UI.Preview.{UiLocation}.Video.Delay" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ahmedmoussa@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -8151,7 +8161,7 @@
 </histogram>
 
 <histogram name="MediaPreviews.UI.Preview.{UiLocation}.Video.ExpectedFPS"
-    units="fps" expires_after="2026-02-01">
+    units="fps" expires_after="2026-04-05">
   <owner>bryantchandler@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -8166,7 +8176,7 @@
 </histogram>
 
 <histogram name="MediaPreviews.UI.Preview.{UiLocation}.Video.RenderedPercent"
-    units="%" expires_after="2026-02-01">
+    units="%" expires_after="2026-04-05">
   <owner>bryantchandler@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -8182,7 +8192,7 @@
 
 <histogram
     name="MediaPreviews.UI.Preview.{UiLocation}.Video.TimeToActionWithoutPreview"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>ahmedmoussa@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -8197,7 +8207,7 @@
 
 <histogram
     name="MediaPreviews.UI.Preview.{UiLocation}.Video.TotalVisibleDuration"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>ahmedmoussa@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -8214,7 +8224,7 @@
 </histogram>
 
 <histogram name="MediaPreviews.UI.Preview.{UiLocation}.VideoCaptureError"
-    units="VideoCaptureError" expires_after="2026-02-01">
+    units="VideoCaptureError" expires_after="2026-04-05">
   <owner>ahmedmoussa@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -8227,7 +8237,7 @@
 </histogram>
 
 <histogram name="MediaPreviews.UI.{UiLocation}.{Type}.Duration" units="s"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ahmedmoussa@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index 59161f9..917909a 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -892,7 +892,7 @@
 
 <histogram
     name="HeapProfiling.InProcess.SampledAddressCacheBucketLengths{Process}"
-    units="nodes" expires_after="2026-02-01">
+    units="nodes" expires_after="2026-04-05">
   <owner>joenotcharles@google.com</owner>
   <owner>chrome-memory@google.com</owner>
   <summary>
@@ -917,7 +917,7 @@
 </histogram>
 
 <histogram name="HeapProfiling.InProcess.SampledAddressCacheHitRate{Process}"
-    units="1/100 %" expires_after="2026-02-01">
+    units="1/100 %" expires_after="2026-04-05">
   <owner>joenotcharles@google.com</owner>
   <owner>chrome-memory@google.com</owner>
   <summary>
@@ -958,7 +958,7 @@
 
 <histogram
     name="HeapProfiling.InProcess.SampledAddressCacheUniformity{Process}"
-    units="%" expires_after="2026-02-01">
+    units="%" expires_after="2026-04-05">
   <owner>joenotcharles@google.com</owner>
   <owner>chrome-memory@google.com</owner>
   <summary>
@@ -1480,7 +1480,7 @@
 </histogram>
 
 <histogram name="Memory.Experimental.Gpu2.Small{ProcessMemoryAllocatorSmall2}"
-    units="KB" expires_after="2026-02-01">
+    units="KB" expires_after="2026-04-05">
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <owner>lizeb@chromium.org</owner>
@@ -1510,7 +1510,7 @@
 </histogram>
 
 <histogram name="Memory.Experimental.Gpu2{ProcessMemoryAllocator2}" units="MB"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <owner>lizeb@chromium.org</owner>
@@ -1746,7 +1746,7 @@
 </histogram>
 
 <histogram name="Memory.Experimental.Renderer2{ProcessMemoryAllocator2}"
-    units="MB" expires_after="2026-02-01">
+    units="MB" expires_after="2026-04-05">
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <owner>lizeb@chromium.org</owner>
@@ -2086,7 +2086,7 @@
 </histogram>
 
 <histogram name="Memory.NativeLibrary.MappedAndResidentMemoryFootprint3"
-    units="KB" expires_after="2026-02-01">
+    units="KB" expires_after="2026-04-05">
   <owner>lizeb@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <summary>
@@ -2751,7 +2751,7 @@
 </histogram>
 
 <histogram name="Memory.RenderProcessHost.Count2.OriginAgentClusterOverhead"
-    units="processes" expires_after="2026-02-01">
+    units="processes" expires_after="2026-04-05">
   <owner>wjmaclean@chromium.org</owner>
   <owner>alexmos@chromium.org</owner>
   <owner>creis@chromium.org</owner>
@@ -2776,7 +2776,7 @@
 </histogram>
 
 <histogram name="Memory.RenderProcessHost.Count2.SandboxedIframeOverhead"
-    units="processes" expires_after="2026-02-01">
+    units="processes" expires_after="2026-04-05">
   <owner>wjmaclean@chromium.org</owner>
   <owner>alexmos@chromium.org</owner>
   <owner>creis@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/mobile/enums.xml b/tools/metrics/histograms/metadata/mobile/enums.xml
index 3c295cc..18618ed1 100644
--- a/tools/metrics/histograms/metadata/mobile/enums.xml
+++ b/tools/metrics/histograms/metadata/mobile/enums.xml
@@ -275,6 +275,7 @@
   <int value="34" label="Image on a Reader Mode page"/>
   <int value="35" label="Image-Link on a Reader Mode page"/>
   <int value="36" label="Link on a Reader Mode page"/>
+  <int value="37" label="Tab Group button in the Overflow Menu"/>
 </enum>
 
 <enum name="IOSShareAction">
diff --git a/tools/metrics/histograms/metadata/mobile/histograms.xml b/tools/metrics/histograms/metadata/mobile/histograms.xml
index 46b432e..c1cc3da8 100644
--- a/tools/metrics/histograms/metadata/mobile/histograms.xml
+++ b/tools/metrics/histograms/metadata/mobile/histograms.xml
@@ -125,7 +125,7 @@
 </histogram>
 
 <histogram name="Mobile.ContextMenu.EntryPoints" enum="IOSMenuScenario"
-    expires_after="2026-01-31">
+    expires_after="2026-04-05">
   <owner>gambard@chromium.org</owner>
   <owner>bling-team@chromium.org</owner>
   <summary>
@@ -236,6 +236,8 @@
     <variant name="TabGroupIndicatorEntry" summary="Tab Group Indicator Entry"/>
     <variant name="TabGroupIndicatorNTPEntry"
         summary="Tab Group Indicator on NTP Entry"/>
+    <variant name="TabGroupOverflowMenu"
+        summary="Tab Group button in the Overflow Menu"/>
     <variant name="TabGroupsPanelEntry" summary="Tab group panel entry"/>
     <variant name="TabGroupViewEntry"
         summary="Tab group view entry in tab grid"/>
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml
index f79e286..3b47d13 100644
--- a/tools/metrics/histograms/metadata/navigation/histograms.xml
+++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -195,7 +195,7 @@
 </histogram>
 
 <histogram name="BackForwardCache.AllSites.HistoryNavigationOutcome"
-    enum="BackForwardCacheHistoryNavigationOutcome" expires_after="2026-02-01">
+    enum="BackForwardCacheHistoryNavigationOutcome" expires_after="2026-04-05">
   <owner>fergal@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -326,7 +326,7 @@
 </histogram>
 
 <histogram name="BackForwardCache.Eviction.Renderer"
-    enum="BackForwardCacheRendererEvictionReason" expires_after="2026-02-01">
+    enum="BackForwardCacheRendererEvictionReason" expires_after="2026-04-05">
   <owner>rakina@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -336,7 +336,7 @@
 </histogram>
 
 <histogram name="BackForwardCache.Eviction.TimeUntilProcessKilled" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>fergal@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -390,7 +390,7 @@
 </histogram>
 
 <histogram name="BackForwardCache.HistoryNavigationOutcome"
-    enum="BackForwardCacheHistoryNavigationOutcome" expires_after="2026-02-01">
+    enum="BackForwardCacheHistoryNavigationOutcome" expires_after="2026-04-05">
   <owner>fergal@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -498,7 +498,7 @@
 </histogram>
 
 <histogram name="BackForwardCache.HistoryNavigationOutcome.NotRestoredReason"
-    enum="BackForwardCacheNotRestoredReason" expires_after="2026-02-01">
+    enum="BackForwardCacheNotRestoredReason" expires_after="2026-04-05">
   <owner>fergal@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -552,7 +552,7 @@
 </histogram>
 
 <histogram name="BackForwardCache.NewPageNavHasPotentialMatch" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>rakina@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -566,7 +566,7 @@
 </histogram>
 
 <histogram name="BackForwardCache.NewPageNavHasPotentialMatchWithNoSubframes"
-    enum="Boolean" expires_after="2026-02-01">
+    enum="Boolean" expires_after="2026-04-05">
   <owner>rakina@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -580,7 +580,7 @@
 </histogram>
 
 <histogram name="BackForwardCache.NonRestoredNavigationDirection"
-    enum="HistoryNavigationDirection" expires_after="2026-02-01">
+    enum="HistoryNavigationDirection" expires_after="2026-04-05">
   <owner>rakina@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -682,7 +682,7 @@
 </histogram>
 
 <histogram name="BackForwardCache.RestoredNavigationDirection"
-    enum="HistoryNavigationDirection" expires_after="2026-02-01">
+    enum="HistoryNavigationDirection" expires_after="2026-04-05">
   <owner>rakina@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -745,7 +745,7 @@
 </histogram>
 
 <histogram name="Navigation.AutomaticBeaconOutcome"
-    enum="AutomaticBeaconOutcome" expires_after="2026-02-01">
+    enum="AutomaticBeaconOutcome" expires_after="2026-04-05">
   <owner>lbrady@google.com</owner>
   <owner>shivanisha@chromium.org</owner>
   <owner>chrome-fenced-frames-core@google.com</owner>
@@ -1067,7 +1067,7 @@
 </histogram>
 
 <histogram name="Navigation.FCPFrameSubmissionToSurfaceEmbed" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>rakina@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -1678,7 +1678,7 @@
 </histogram>
 
 <histogram name="Navigation.OnBeforeUnloadOverheadTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>clamy@chromium.org</owner>
   <owner>nasko@chromium.org</owner>
   <summary>
@@ -1878,6 +1878,24 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Navigation.Prefetch.IsPrefetchingSRPUrl.{PreloadingEmbedderType}"
+    enum="Boolean" expires_after="2026-03-22">
+  <owner>robertlin@chromium.org</owner>
+  <owner>chrome-prerendering@google.com</owner>
+  <summary>
+    To record whether a prefetching attempt initiated by
+    {PreloadingEmbedderType} trigger is trying to prefetch a SRP url or not.
+
+    This is recorded when
+    {PreloadingEmbedderType}PreloadPipelineManager::StartPrefetch is called to
+    initiate a SPR url.
+  </summary>
+  <token key="PreloadingEmbedderType">
+    <variant name="Embedder_BookmarkBar" summary="BookmarkBar"/>
+  </token>
+</histogram>
+
 <histogram name="Navigation.Prefetch.PrefetchJobQueueingTime" units="ms"
     expires_after="2023-03-19">
   <owner>blundell@chromium.org</owner>
@@ -2304,7 +2322,7 @@
 </histogram>
 
 <histogram name="Navigation.StartAdjustment.AllFrames"
-    enum="NavigationStartAdjustmentType" expires_after="2026-02-03">
+    enum="NavigationStartAdjustmentType" expires_after="2026-04-05">
   <owner>creis@chromium.org</owner>
   <owner>alexmos@chromium.org</owner>
   <summary>
@@ -2423,7 +2441,7 @@
 </histogram>
 
 <histogram name="Navigation.StartAdjustment.MainFrameOnly"
-    enum="NavigationStartAdjustmentType" expires_after="2026-02-03">
+    enum="NavigationStartAdjustmentType" expires_after="2026-04-05">
   <owner>creis@chromium.org</owner>
   <owner>alexmos@chromium.org</owner>
   <summary>
@@ -2471,7 +2489,7 @@
 </histogram>
 
 <histogram name="Navigation.SurfaceEmbedToFCPFrameSubmission" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>rakina@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <summary>
@@ -2877,7 +2895,7 @@
 
 <histogram
     name="Navigation.URLLoader.OnAcceptCHFrameReceived.CriticalHintsMissingStatus"
-    enum="CriticalHintsMissingStatus" expires_after="2026-02-01">
+    enum="CriticalHintsMissingStatus" expires_after="2026-04-05">
   <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -2928,7 +2946,7 @@
 </histogram>
 
 <histogram name="Navigation.URLLoader.OnAcceptCHFrameReceived.ExecutionTime"
-    units="microseconds" expires_after="2026-02-01">
+    units="microseconds" expires_after="2026-04-05">
   <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -2960,7 +2978,7 @@
 </histogram>
 
 <histogram name="Navigation.URLLoader.OnAcceptCHFrameReceived.ReturnLocation"
-    enum="OnAcceptCHFrameReceivedReturnLocation" expires_after="2026-02-01">
+    enum="OnAcceptCHFrameReceivedReturnLocation" expires_after="2026-04-05">
   <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -3154,7 +3172,7 @@
 
 <histogram
     name="NavigationPredictor.SearchEnginePreconnector.ConsecutiveFailures"
-    units="Consecutive failures" expires_after="2026-02-01">
+    units="Consecutive failures" expires_after="2026-04-05">
   <owner>suzukikeita@google.com</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -3167,7 +3185,7 @@
 
 <histogram
     name="NavigationPredictor.SearchEnginePreconnector.PreconnectAttemptInterval"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>suzukikeita@google.com</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -3178,7 +3196,7 @@
 </histogram>
 
 <histogram name="NavigationPredictor.SearchEnginePreconnector.PreconnectDelay"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>suzukikeita@google.com</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -3190,7 +3208,7 @@
 </histogram>
 
 <histogram name="NavigationPredictor.SearchEnginePreconnector.TriggerEvent"
-    enum="PreconnectTriggerEvent" expires_after="2026-02-01">
+    enum="PreconnectTriggerEvent" expires_after="2026-04-05">
   <owner>suzukikeita@google.com</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -3651,7 +3669,7 @@
 </histogram>
 
 <histogram name="Prerender.Experimental.PrewarmDecision"
-    enum="PrerenderPrewarmDecision" expires_after="2025-11-01">
+    enum="PrerenderPrewarmDecision" expires_after="2026-10-01">
   <owner>toyoshim@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -3758,17 +3776,17 @@
   <owner>robertlin@chromium.org</owner>
   <owner>chrome-prerendering@google.com</owner>
   <summary>
-    To record whether a prerendering attempt initiated by
-    {PreloadingEmbedderType} is trying to prerender a SRP url or not.
+    To record whether a prerendering attempt initiated by prerender
+    {PreloadingEmbedderType} trigger is trying to prerender a SRP url or not.
 
-    This is recorded when PrerenderManager::StartPrerender* is called to
-    initiate {PreloadingEmbedderType}, which is the list of affected prerender
-    triggers in https://crbug.com/40066983.
+    This is recorded when
+    {PreloadingEmbedderType}PreloadPipelineManager::StartPrerender is called to
+    initiate a SPR url, which is the list of affected prerender triggers in
+    https://crbug.com/40066983.
   </summary>
   <token key="PreloadingEmbedderType">
-    <variant name="Embedder_BookmarkBar"
-        summary="Prerender BookmarkBar Trigger"/>
-    <variant name="Embedder_NewTabPage" summary="Prerender NewTabPage Trigger"/>
+    <variant name="Embedder_BookmarkBar" summary="BookmarkBar"/>
+    <variant name="Embedder_NewTabPage" summary="NewTabPage"/>
   </token>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index 881dd8a..286e158 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -165,6 +165,24 @@
   <variant name="WriteEntryData"/>
 </variants>
 
+<!-- LINT.IfChange(StreamSocketCloseReason) -->
+
+<variants name="StreamSocketCloseReason">
+  <variant name="Abort"/>
+  <variant name="AttemptManagerDraining"/>
+  <variant name="CannotUseTcpBasedProtocols"/>
+  <variant name="CloseAllConnections"/>
+  <variant name="IpAddressChanged"/>
+  <variant name="QuicSessionCreated"/>
+  <variant name="SpdySessionCreated"/>
+  <variant name="SslConfigChanged"/>
+  <variant name="Unspecified"/>
+  <variant name="UsingExistingQuicSession"/>
+  <variant name="UsingExistingSpdySession"/>
+</variants>
+
+<!-- LINT.ThenChange(//net/socket/stream_socket_close_reason.h:StreamSocketCloseReason) -->
+
 <variants name="SuccessOrFailure">
   <variant name="Failure" summary="Operation failed"/>
   <variant name="Success" summary="Operation succeeded"/>
@@ -457,7 +475,7 @@
 </histogram>
 
 <histogram name="Crypto.UnexportableKeys.BackgroundTaskResult{TaskType}"
-    enum="UnexportableKeyServiceResult" expires_after="2026-02-01">
+    enum="UnexportableKeyServiceResult" expires_after="2026-04-05">
   <owner>alexilin@chromium.org</owner>
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
@@ -480,7 +498,7 @@
 
 <histogram
     name="Crypto.UnexportableKeys.BackgroundTaskRetries{TaskType}{Success}"
-    units="retries" expires_after="2026-02-01">
+    units="retries" expires_after="2026-04-05">
   <owner>alexilin@chromium.org</owner>
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
@@ -636,7 +654,7 @@
 </histogram>
 
 <histogram name="HttpCache.Experimental.Read.EntryContentSize" units="bytes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>anthonyvd@chromium.org</owner>
   <owner>chrome-catan@google.com</owner>
   <summary>
@@ -646,7 +664,7 @@
 </histogram>
 
 <histogram name="HttpCache.Experimental.Read.EntryResponseInfoSize"
-    units="bytes" expires_after="2026-02-01">
+    units="bytes" expires_after="2026-04-05">
   <owner>anthonyvd@chromium.org</owner>
   <owner>chrome-catan@google.com</owner>
   <summary>
@@ -656,7 +674,7 @@
 </histogram>
 
 <histogram name="HttpCache.Experimental.Read.EntryTotalSize" units="bytes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>anthonyvd@chromium.org</owner>
   <owner>chrome-catan@google.com</owner>
   <summary>
@@ -933,7 +951,7 @@
 </histogram>
 
 <histogram name="HttpCache.NoVarySearch.LookupTime{HitOrMiss}{MainFrameOrNot}"
-    units="microseconds" expires_after="2026-02-02">
+    units="microseconds" expires_after="2026-04-05">
   <owner>ricea@chromium.org</owner>
   <owner>net-dev@chromium.org</owner>
   <summary>
@@ -1278,7 +1296,7 @@
 </histogram>
 
 <histogram name="Net.AcceptCHFrameInterceptor.NeedsObserverCheckReason"
-    enum="NeedsObserverCheckReason" expires_after="2026-02-01">
+    enum="NeedsObserverCheckReason" expires_after="2026-04-05">
   <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -2295,7 +2313,7 @@
 </histogram>
 
 <histogram name="Net.DNS.DnsTransaction.Insecure.{DohProviderId}FailureTime"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>awillia@chromium.org</owner>
   <owner>src/net/dns/OWNERS</owner>
   <summary>
@@ -2305,7 +2323,7 @@
 </histogram>
 
 <histogram name="Net.DNS.DnsTransaction.Insecure.{DohProviderId}SuccessTime"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>awillia@chromium.org</owner>
   <owner>src/net/dns/OWNERS</owner>
   <summary>
@@ -2316,7 +2334,7 @@
 
 <histogram
     name="Net.DNS.DnsTransaction.SecureNotValidated.{DohProviderId}FailureError"
-    enum="NetErrorCodes" expires_after="2026-02-01">
+    enum="NetErrorCodes" expires_after="2026-04-05">
   <owner>awillia@chromium.org</owner>
   <owner>src/net/dns/OWNERS</owner>
   <summary>
@@ -2379,7 +2397,7 @@
 
 <histogram
     name="Net.DNS.DnsTransaction.SecureValidated.{DohProviderId}SuccessTime"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>awillia@chromium.org</owner>
   <owner>src/net/dns/OWNERS</owner>
   <summary>
@@ -2501,7 +2519,7 @@
 </histogram>
 
 <histogram name="Net.DNS.HTTPSSVC.RecordHttps.{secure}.ExpectNoerror.DnsRcode"
-    enum="HttpssvcDnsRcode" expires_after="2026-02-01">
+    enum="HttpssvcDnsRcode" expires_after="2026-04-05">
   <owner>bashi@chromium.org</owner>
   <owner>davidben@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
@@ -2722,7 +2740,7 @@
 </histogram>
 
 <histogram name="Net.DNS.ResolveContext.DohAutoupgrade.{DohProviderId}Status"
-    enum="DohServerAutoupgradeStatus" expires_after="2026-02-01">
+    enum="DohServerAutoupgradeStatus" expires_after="2026-04-05">
   <owner>awillia@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -3356,7 +3374,7 @@
 </histogram>
 
 <histogram name="Net.HttpRequestStsState" enum="HttpRequestStsState"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>jdeblasio@chromium.org</owner>
   <owner>trusty-transport@chromium.org</owner>
   <summary>
@@ -3414,7 +3432,7 @@
 </histogram>
 
 <histogram name="Net.HttpStreamPool.InitialAttemptState2"
-    enum="HttpStreamPoolInitialAttemptState" expires_after="2026-02-01">
+    enum="HttpStreamPoolInitialAttemptState" expires_after="2026-04-05">
   <owner>bashi@chromium.org</owner>
   <owner>blink-network-stack@google.com</owner>
   <summary>
@@ -3425,7 +3443,7 @@
 </histogram>
 
 <histogram name="Net.HttpStreamPool.JobCompleteTime3.{Result}" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>bashi@chromium.org</owner>
   <owner>blink-network-stack@google.com</owner>
   <summary>
@@ -3488,7 +3506,7 @@
 </histogram>
 
 <histogram name="Net.HttpStreamPool.QuicAttemptTime.{Result}" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>bashi@chromium.org</owner>
   <owner>blink-network-stack@google.com</owner>
   <summary>
@@ -3510,17 +3528,7 @@
     The number of in-flight stream attempts when canceling them. Recorded when
     an HttpStreamPool::AttemptManager cancels in-flight stream attempts.
   </summary>
-  <token key="Reason">
-    <variant name="CannotUseTcpBasedProtocols"/>
-    <variant name="CloseAllConnections"/>
-    <variant name="IpAddressChanged"/>
-    <variant name="QuicSessionCreated"/>
-    <variant name="SpdySessionCreated"/>
-    <variant name="SslConfigChanged"/>
-    <variant name="Unspecified"/>
-    <variant name="UsingExistingQuicSession"/>
-    <variant name="UsingExistingSpdySession"/>
-  </token>
+  <token key="Reason" variants="StreamSocketCloseReason"/>
 </histogram>
 
 <histogram
@@ -3571,7 +3579,7 @@
 </histogram>
 
 <histogram name="Net.HttpStreamPool.TcpBasedAttemptDelay" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>bashi@chromium.org</owner>
   <owner>blink-network-stack@google.com</owner>
   <summary>
@@ -3608,6 +3616,22 @@
   </token>
 </histogram>
 
+<histogram name="Net.HttpStreamPool.{JobType}JobCancelCount.{Reason}"
+    units="jobs" expires_after="2025-12-02">
+  <owner>bashi@chromium.org</owner>
+  <owner>blink-network-stack@google.com</owner>
+  <summary>
+    The number of HttpStreamPool::Jobs when an HttpStreamPool::AttemptManager
+    cancels them. Recorded when an HttpStreamPool::AttemptManager cancels
+    HttpStreamPool::Jobs.
+  </summary>
+  <token key="Reason" variants="StreamSocketCloseReason"/>
+  <token key="JobType">
+    <variant name="Preconnect"/>
+    <variant name="Request"/>
+  </token>
+</histogram>
+
 <histogram name="Net.HttpTimeToFirstByte.TLS13.Google" units="ms"
     expires_after="2026-03-01">
   <owner>davidben@chromium.org</owner>
@@ -3703,7 +3727,7 @@
 </histogram>
 
 <histogram name="Net.NetworkTransaction.ConnectedCallbackDelay" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hayato@chromium.org</owner>
   <owner>blink-network-stack@google.com</owner>
   <summary>
@@ -5542,7 +5566,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.NumReceivedOrigins" units="origins"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>fayang@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -5554,7 +5578,7 @@
 
 <histogram
     name="Net.QuicSession.NumStreamsWaitingToWriteOnIdleTimeout{HostType}"
-    units="streams" expires_after="2026-02-01">
+    units="streams" expires_after="2026-04-05">
   <owner>renjietang@chromium.org</owner>
   <owner>suzukikeita@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
@@ -5763,7 +5787,7 @@
 
 <histogram
     name="Net.QuicSession.PathValidationSuccess{ConnectionMigrationCause}"
-    enum="BooleanSuccess" expires_after="2026-02-01">
+    enum="BooleanSuccess" expires_after="2026-04-05">
   <owner>renjietang@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -6812,7 +6836,7 @@
 
 <histogram
     name="Net.RestrictedCookieManager.SetCookieFromString.Duration.Subsampled"
-    units="microseconds" expires_after="2026-02-01">
+    units="microseconds" expires_after="2026-04-05">
   <owner>acondor@chromium.org</owner>
   <owner>clank-performance-team@google.com</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -7376,7 +7400,7 @@
 </histogram>
 
 <histogram name="Net.SpdySession.AlpsDecoderStatus.Bypassed"
-    enum="AlpsDecoderError" expires_after="2026-02-01">
+    enum="AlpsDecoderError" expires_after="2026-04-05">
   <owner>arichiv@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -8026,7 +8050,7 @@
 </histogram>
 
 <histogram name="Net.TCPSocket.PortReuseTimeWindows2.{IPType}.{Result}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>arichiv@chromium.org</owner>
   <owner>awillia@chromium.org</owner>
   <owner>katabolism-finch@google.com</owner>
@@ -8177,7 +8201,7 @@
 </histogram>
 
 <histogram name="Net.URLLoader.AcceptCH.NeedsObserverCheck" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -8190,7 +8214,7 @@
 </histogram>
 
 <histogram name="Net.URLLoader.AcceptCH.RoundTripTime" units="microseconds"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
@@ -8218,7 +8242,7 @@
 </histogram>
 
 <histogram name="Net.URLLoader.AcceptCH.Status" enum="NetErrorCodes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index bcd4e942..78fbfbbb 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -5150,7 +5150,7 @@
 </histogram>
 
 <histogram name="NetworkService.ParsedHeaders.IPCHandleTime"
-    units="microseconds" expires_after="2026-02-01">
+    units="microseconds" expires_after="2026-04-05">
   <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
index 567117f0..282a6dd 100644
--- a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
@@ -345,7 +345,7 @@
 </histogram>
 
 <histogram name="NewTabPage.Composebox.Query.Modality.V2"
-    enum="NtpComposeboxMultimodalState" expires_after="2026-01-20">
+    enum="NtpComposeboxMultimodalState" expires_after="2026-04-05">
   <owner>jennserrano@google.com</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
   <owner>chrome-desktop-search@google.com</owner>
@@ -567,7 +567,7 @@
 </histogram>
 
 <histogram name="NewTabPage.Composeplate.Impression" enum="BooleanVisible"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hanxi@chromium.org</owner>
   <owner>xinyiji@chromium.org</owner>
   <summary>
@@ -856,7 +856,7 @@
 </histogram>
 
 <histogram name="NewTabPage.FakeSearchBox.ComposeplateButton.Impression"
-    units="count" expires_after="2026-02-01">
+    units="count" expires_after="2026-04-05">
   <owner>hanxi@chromium.org</owner>
   <owner>xinyiji@chromium.org</owner>
   <summary>
@@ -867,7 +867,7 @@
 </histogram>
 
 <histogram name="NewTabPage.FakeSearchBox.Impression" units="count"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hanxi@chromium.org</owner>
   <owner>xinyiji@chromium.org</owner>
   <summary>
@@ -920,7 +920,7 @@
 </histogram>
 
 <histogram name="NewTabPage.Footer.ShownTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>pauladedeji@google.com</owner>
   <owner>tiborg@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -931,7 +931,7 @@
 </histogram>
 
 <histogram name="NewTabPage.Footer.ToggledVisibility{FooterToggleSource}"
-    enum="BooleanVisible" expires_after="2026-02-01">
+    enum="BooleanVisible" expires_after="2026-04-05">
   <owner>pauladedeji@google.com</owner>
   <owner>tiborg@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -948,7 +948,7 @@
 </histogram>
 
 <histogram name="NewTabPage.Footer.VisibleOnLoad" enum="BooleanVisible"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>pauladedeji@google.com</owner>
   <owner>tiborg@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -958,7 +958,7 @@
 </histogram>
 
 <histogram name="NewTabPage.Footer.WebUI.LoadCompletedTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>pauladedeji@google.com</owner>
   <owner>tiborg@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -970,7 +970,7 @@
 </histogram>
 
 <histogram name="NewTabPage.Footer.WebUI.LoadDocumentTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>pauladedeji@google.com</owner>
   <owner>tiborg@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -1197,7 +1197,7 @@
 </histogram>
 
 <histogram name="NewTabPage.LogoShown{NewTabPageLogoShownFromCache}"
-    enum="NewTabPageLogoShown" expires_after="2026-02-01">
+    enum="NewTabPageLogoShown" expires_after="2026-04-05">
   <owner>romanarora@chromium.org</owner>
   <owner>tiborg@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -1359,7 +1359,7 @@
 </histogram>
 
 <histogram name="NewTabPage.Module.Click" enum="ModuleTypeOnStartAndNTP"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hanxi@chromium.org</owner>
   <owner>xinyiji@chromium.org</owner>
   <summary>
@@ -1898,7 +1898,7 @@
 </histogram>
 
 <histogram name="NewTabPage.NumberOfTiles" units="units"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>romanarora@chromium.org</owner>
   <owner>tiborg@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -1971,7 +1971,7 @@
 </histogram>
 
 <histogram name="NewTabPage.OutlookCalendar.RequestResult"
-    enum="OutlookCalendarRequestResult" expires_after="2026-01-20">
+    enum="OutlookCalendarRequestResult" expires_after="2026-04-05">
   <owner>jennserrano@google.com</owner>
   <owner>rtatum@google.com</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -1984,7 +1984,7 @@
 </histogram>
 
 <histogram name="NewTabPage.OutlookCalendar.ResponseResult" units="count"
-    expires_after="2026-01-20">
+    expires_after="2026-04-05">
   <owner>jennserrano@google.com</owner>
   <owner>rtatum@google.com</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -2269,7 +2269,7 @@
 </histogram>
 
 <histogram name="NewTabPage.TabResumption.ResultStatus"
-    enum="NtpTabResumeResultStatus" expires_after="2026-02-01">
+    enum="NtpTabResumeResultStatus" expires_after="2026-04-05">
   <owner>mfacey@google.com</owner>
   <owner>romanarora@google.com</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
@@ -2314,7 +2314,7 @@
 </histogram>
 
 <histogram name="NewTabPage.TabResumption.URLVisitAggregateDataTypeDisplayed"
-    enum="NtpURLVisitAggregateTypes" expires_after="2026-02-01">
+    enum="NtpURLVisitAggregateTypes" expires_after="2026-04-05">
   <owner>mfacey@google.com</owner>
   <owner>romanarora@google.com</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/notifications/histograms.xml b/tools/metrics/histograms/metadata/notifications/histograms.xml
index d189676..c92c1f6 100644
--- a/tools/metrics/histograms/metadata/notifications/histograms.xml
+++ b/tools/metrics/histograms/metadata/notifications/histograms.xml
@@ -630,7 +630,7 @@
 </histogram>
 
 <histogram name="Notifications.Engagement.{Type}.Volume{Volume}"
-    units="site-engagement-score*2" expires_after="2026-02-01">
+    units="site-engagement-score*2" expires_after="2026-04-05">
   <owner>antoniosartori@chromium.org</owner>
   <owner>feuunk@chromium.org</owner>
   <summary>
@@ -994,7 +994,7 @@
 </histogram>
 
 <histogram name="Notifications.Windows.ActivationStatus"
-    enum="WindowsNotificationActivationStatus" expires_after="2026-02-01">
+    enum="WindowsNotificationActivationStatus" expires_after="2026-04-05">
   <owner>finnur@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/omnibox/histograms.xml b/tools/metrics/histograms/metadata/omnibox/histograms.xml
index 969c780..49ef820 100644
--- a/tools/metrics/histograms/metadata/omnibox/histograms.xml
+++ b/tools/metrics/histograms/metadata/omnibox/histograms.xml
@@ -385,8 +385,8 @@
   <owner>chrome-desktop-search@google.com</owner>
   <summary>
     The AIM Eligibility service makes HTTP requests to determine which AIM
-    features the user is eligible for. The requests are triggered on startup and
-    when the state of accounts in the cookie jar changes.
+    features the user is eligible for. The requests are triggered on profile
+    startup and when the primary account changes.
 
     This metric logs the status of the request, sliced by request source.
   </summary>
@@ -400,13 +400,13 @@
   <owner>chrome-desktop-search@google.com</owner>
   <summary>
     The AIM Eligibility service makes HTTP requests to determine which AIM
-    features the user is eligible for. The requests are triggered on startup and
-    when the state of accounts in the cookie jar changes.
+    features the user is eligible for. The requests are triggered on profile
+    startup and when the primary account changes.
 
-    This metric logs whether the response contained a &quot;is_eligible&quot;
-    field set to true. If the field is missing or false, this metric will log
-    false. If the response fails (e.g. server is down or response is poorly
-    formatted), this metric will not be logged.
+    This metric is logged only when the server response changes compared to the
+    previous value (e.g., the value of the &quot;is_eligible&quot; field differs
+    from the last logged response). If the response fails (e.g. server is down
+    or response is poorly formatted), this metric will not be logged.
   </summary>
 </histogram>
 
@@ -418,14 +418,32 @@
   <owner>chrome-desktop-search@google.com</owner>
   <summary>
     The AIM Eligibility service makes HTTP requests to determine which AIM
-    features the user is eligible for. The requests are triggered on startup and
-    when the state of accounts in the cookie jar changes.
+    features the user is eligible for. The requests are triggered on profile
+    startup and when the primary account changes.
 
     This metric is logged only when the server response changes compared to the
     previous value (e.g., the value of the &quot;is_pdf_upload_eligible&quot;
-    field differs from the last logged response). If the field is missing or
-    false, this metric will log false. If the response fails (e.g. server is
-    down or response is poorly formatted), this metric will not be logged.
+    field differs from the last logged response). If the response fails (e.g.
+    server is down or response is poorly formatted), this metric will not be
+    logged.
+  </summary>
+</histogram>
+
+<histogram
+    name="Omnibox.AimEligibility.EligibilityResponseChange.session_index"
+    units="index" expires_after="2026-08-05">
+  <owner>mahmadi@chromium.org</owner>
+  <owner>manukh@chromium.org</owner>
+  <owner>chrome-desktop-search@google.com</owner>
+  <summary>
+    The AIM Eligibility service makes HTTP requests to determine which AIM
+    features the user is eligible for. The requests are triggered on profile
+    startup and when the primary account changes.
+
+    This metric is logged only when the server response changes compared to the
+    previous value (e.g., the value of the &quot;session_index&quot; field
+    differs from the last logged response). If the response fails (e.g. server
+    is down or response is poorly formatted), this metric will not be logged.
   </summary>
 </histogram>
 
@@ -436,8 +454,8 @@
   <owner>chrome-desktop-search@google.com</owner>
   <summary>
     The AIM Eligibility service makes HTTP requests to determine which AIM
-    features the user is eligible for. The requests are triggered on startup and
-    when the state of accounts in the cookie jar changes.
+    features the user is eligible for. The requests are triggered on profile
+    startup and when the primary account changes.
 
     This metric logs the HTTP response code, sliced by request source.
   </summary>
@@ -451,8 +469,8 @@
   <owner>chrome-desktop-search@google.com</owner>
   <summary>
     The AIM Eligibility service makes HTTP requests to determine which AIM
-    features the user is eligible for. The requests are triggered on startup and
-    when the state of accounts in the cookie jar changes.
+    features the user is eligible for. The requests are triggered on profile
+    startup and when the primary account changes.
 
     This metric logs the source of the eligibility response when the service is
     queried by its clients for the server eligibility status. It helps determine
@@ -469,8 +487,8 @@
   <owner>chrome-desktop-search@google.com</owner>
   <summary>
     The AIM Eligibility service makes HTTP requests to determine which AIM
-    features the user is eligible for. The requests are triggered on startup and
-    when the state of accounts in the cookie jar changes.
+    features the user is eligible for. The requests are triggered on profile
+    startup and when the primary account changes.
 
     This metric logs whether the response contained a &quot;is_eligible&quot;
     field set to true, sliced by request source. If the field is missing or
@@ -488,8 +506,8 @@
   <owner>chrome-desktop-search@google.com</owner>
   <summary>
     The AIM Eligibility service makes HTTP requests to determine which AIM
-    features the user is eligible for. The requests are triggered on startup and
-    when the state of accounts in the cookie jar changes.
+    features the user is eligible for. The requests are triggered on profile
+    startup and when the primary account changes.
 
     This metric logs whether the response contained a
     &quot;is_pdf_upload_eligible&quot; field set to true, sliced by request
@@ -500,6 +518,26 @@
   <token key="RequestSource" variants="AimEligibilityRequestSource"/>
 </histogram>
 
+<histogram
+    name="Omnibox.AimEligibility.EligibilityResponse{RequestSource}.session_index"
+    units="index" expires_after="2026-08-05">
+  <owner>mahmadi@chromium.org</owner>
+  <owner>manukh@chromium.org</owner>
+  <owner>chrome-desktop-search@google.com</owner>
+  <summary>
+    The AIM Eligibility service makes HTTP requests to determine which AIM
+    features the user is eligible for. The requests are triggered on profile
+    startup and when the primary account changes.
+
+    This metric logs the selected session index (index of the account in the
+    cookie jar selected by the server for authentication), sliced by request
+    source. If the field is missing or false, this metric will log false. If the
+    response fails (e.g. server is down or response is poorly formatted), this
+    metric will not be logged.
+  </summary>
+  <token key="RequestSource" variants="AimEligibilityRequestSource"/>
+</histogram>
+
 <histogram name="Omnibox.AimEntrypoint.Activated.UserTextPresent"
     enum="Boolean" expires_after="2026-03-08">
   <owner>khalidpeer@chromium.org</owner>
@@ -518,7 +556,7 @@
 
 <histogram
     name="Omnibox.AimEntrypoint.Activated.UserTextPresent.ByPageContext.{OmniboxPageContext}"
-    enum="Boolean" expires_after="2025-12-15">
+    enum="Boolean" expires_after="2026-04-05">
   <owner>khalidpeer@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <owner>chrome-desktop-search@google.com</owner>
@@ -588,7 +626,7 @@
 
 <histogram
     name="Omnibox.AimEntrypoint.Shown.ByPageContext.{OmniboxPageContext}"
-    enum="Boolean" expires_after="2025-12-15">
+    enum="Boolean" expires_after="2026-04-05">
   <owner>khalidpeer@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <owner>chrome-desktop-search@google.com</owner>
@@ -1330,7 +1368,7 @@
 
 <histogram name="Omnibox.DsePreload.Prefetch.NoVarySearchDataCacheUpdate"
     enum="SearchPreloadServiceNoVarySearchDataCacheUpdate"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>kenoss@chromium.org</owner>
   <owner>chrome-prerendering@google.com</owner>
   <summary>
@@ -2383,7 +2421,7 @@
 </histogram>
 
 <histogram name="Omnibox.OnPopupOpen.PaywallSignal" enum="PaywallSignal"
-    expires_after="2026-01-31">
+    expires_after="2026-04-05">
   <owner>juanmojica@google.com</owner>
   <owner>mercerd@google.com</owner>
   <owner>chrome-desktop-search@google.com</owner>
@@ -2779,7 +2817,7 @@
 </histogram>
 
 <histogram name="Omnibox.SearchPrefetch.Within1sDuplicateSearchTermsAge"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>lingqi@chromium.org</owner>
   <owner>nhiroki@chromium.org</owner>
   <owner>chrome-prerendering@google.com</owner>
@@ -3398,7 +3436,7 @@
 
 <histogram
     name="Omnibox.SuggestRequestsSent.ResponseTime2.RequestState{RemoteRequestType}{RemoteRequestCompleted}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>ananyaseelam@google.com</owner>
   <owner>manukh@chromium.org</owner>
   <owner>chrome-desktop-search@google.com</owner>
@@ -3439,7 +3477,7 @@
 
 <histogram
     name="Omnibox.SuggestRequestsSent.ResultCount.EnterpriseSearchAggregatorSuggest{EnterpriseSuggestionType}"
-    units="count" expires_after="2026-02-01">
+    units="count" expires_after="2026-04-05">
   <owner>ananyaseelam@chromium.org</owner>
   <owner>manukh@chromium.org</owner>
   <owner>chrome-desktop-search@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/on_device_model/histograms.xml b/tools/metrics/histograms/metadata/on_device_model/histograms.xml
index 947d1fc8..c7ff925f 100644
--- a/tools/metrics/histograms/metadata/on_device_model/histograms.xml
+++ b/tools/metrics/histograms/metadata/on_device_model/histograms.xml
@@ -174,7 +174,7 @@
 </histogram>
 
 <histogram name="OnDeviceModel.GpuErrorReason" enum="GpuErrorReason"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>cduvall@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml
index ceb2630f..a2d9c17 100644
--- a/tools/metrics/histograms/metadata/optimization/histograms.xml
+++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -152,9 +152,15 @@
   <variant name="PassageEmbedder" summary="Text embedding for passages"/>
   <variant name="PasswordManagerFormClassification"
       summary="Classifies password forms"/>
+  <variant name="PermissionsAiv4GeolocationAndroid"
+      summary="Permissions: Use snapshot and visible page content text to
+               decide on relevance for a Geolocation permission request"/>
   <variant name="PermissionsAiv4GeolocationDesktop"
       summary="Permissions: Use snapshot and visible page content text to
                decide on relevance for a Geolocation permission request"/>
+  <variant name="PermissionsAiv4NotificationsAndroid"
+      summary="Permissions: Use snapshot and visible page content text to
+               decide on relevance for a Notifications permission request"/>
   <variant name="PermissionsAiv4NotificationsDesktop"
       summary="Permissions: Use snapshot and visible page content text to
                decide on relevance for a Notifications permission request"/>
@@ -587,7 +593,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.AIPageContent.TotalLatency" units="ms"
-    expires_after="2026-03-29">
+    expires_after="2026-04-05">
   <owner>khushalsagar@chromium.org</owner>
   <owner>abigailbklein@google.com</owner>
   <summary>
@@ -1010,7 +1016,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.ModelExecution.OnDeviceBaseModelLoadResult"
-    enum="LoadPlatformModelStatus" expires_after="2026-02-01">
+    enum="LoadPlatformModelStatus" expires_after="2026-04-05">
   <owner>rajendrant@chromium.org</owner>
   <owner>holte@chromium.org</owner>
   <summary>
@@ -1210,7 +1216,7 @@
 
 <histogram
     name="OptimizationGuide.ModelExecution.OnDeviceModelValidationResultOnValidationStarted"
-    enum="OnDeviceModelValidationResult" expires_after="2026-02-01">
+    enum="OnDeviceModelValidationResult" expires_after="2026-04-05">
   <owner>cduvall@chromium.org</owner>
   <owner>holte@chromium.org</owner>
   <summary>
@@ -1308,7 +1314,7 @@
 
 <histogram
     name="OptimizationGuide.ModelExecution.SessionUsedRemoteExecution.{ModelExecutionFeature}"
-    enum="Boolean" expires_after="2026-02-01">
+    enum="Boolean" expires_after="2026-04-05">
   <owner>holte@chromium.org</owner>
   <owner>wittman@chromium.org</owner>
   <summary>
@@ -1555,7 +1561,7 @@
 
 <histogram
     name="OptimizationGuide.ModelHandler.HandlerCreated.{OptimizationTarget}"
-    enum="BooleanCreated" expires_after="2026-02-01">
+    enum="BooleanCreated" expires_after="2026-04-05">
   <owner>wittman@chromium.org</owner>
   <owner>rajendrant@chromium.org</owner>
   <summary>
@@ -1879,7 +1885,7 @@
 <histogram
     name="OptimizationGuide.PredictionModelDownloadManager.DownloadStatus"
     enum="OptimizationGuidePredictionModelDownloadStatus"
-    expires_after="2025-12-28">
+    expires_after="2026-04-05">
   <owner>rajendrant@chromium.org</owner>
   <owner>wittman@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 59d5435..9810b8af 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -2075,7 +2075,7 @@
 </histogram>
 
 <histogram name="Ads.InterestGroup.Round.GroupsUpdated.TotalCount"
-    units="groups" expires_after="2026-01-31">
+    units="groups" expires_after="2026-04-05">
   <owner>xtlsheep@google.com</owner>
   <owner>privacy-sandbox-dev@chromium.org</owner>
   <summary>
@@ -4073,7 +4073,7 @@
 </histogram>
 
 <histogram name="DisplayManager.InternalDisplayZoomPercentage" units="%"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>zhangwenyu@google.com</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
@@ -4112,7 +4112,7 @@
 </histogram>
 
 <histogram name="DisplayManager.MultiDisplayMode" enum="MultiDisplayModes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>zhangwenyu@google.com</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
@@ -4984,7 +4984,7 @@
 </histogram>
 
 <histogram name="Feedback.RequestSource" enum="FeedbackSource"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>afakhry@chromium.org</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>Records the source that requested showing the feedback app.</summary>
@@ -5614,7 +5614,7 @@
 </histogram>
 
 <histogram name="Hwsec.Attestation.PrepareDuration" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>chingkang@chromium.org</owner>
   <owner>cros-hwsec-userland-eng+uma@google.com</owner>
   <summary>The duration of preparation for enrollment in attestation.</summary>
@@ -6377,7 +6377,7 @@
 </histogram>
 
 <histogram name="NativeSmbFileShare.AuthenticationMethod"
-    enum="NativeSmbFileShare_AuthMethod" expires_after="2026-02-01">
+    enum="NativeSmbFileShare_AuthMethod" expires_after="2026-04-05">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/chrome/browser/ash/smb_client/OWNERS</owner>
   <summary>
@@ -6774,7 +6774,7 @@
 </histogram>
 
 <histogram name="OSCrypt.BackendUsage" enum="LinuxPasswordStoreUsage"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>vasilii@chromium.org</owner>
   <owner>mamir@chromium.org</owner>
   <summary>
@@ -6866,7 +6866,7 @@
 </histogram>
 
 <histogram name="OSCrypt.Mac.FindGenericPasswordError"
-    enum="MacSecurityFrameworkOSStatus" expires_after="2026-02-01">
+    enum="MacSecurityFrameworkOSStatus" expires_after="2026-04-05">
   <owner>avi@chromium.org</owner>
   <owner>lgrey@chromium.org</owner>
   <owner>markrowe@chromium.org</owner>
@@ -6884,7 +6884,7 @@
 </histogram>
 
 <histogram name="OSCrypt.Mac.FindGenericPasswordResult"
-    enum="FindGenericPasswordResult" expires_after="2026-02-01">
+    enum="FindGenericPasswordResult" expires_after="2026-04-05">
   <owner>avi@chromium.org</owner>
   <owner>lgrey@chromium.org</owner>
   <owner>markrowe@chromium.org</owner>
@@ -7608,7 +7608,7 @@
 </histogram>
 
 <histogram name="PushMessaging.DeliveryStatus" enum="PushEventStatus"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>peter@chromium.org</owner>
   <owner>knollr@chromium.org</owner>
   <summary>
@@ -8103,7 +8103,7 @@
 </histogram>
 
 <histogram name="SB2.RemoteCall.CanCheckUrl" enum="BooleanCanCheckUrl"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -8371,7 +8371,7 @@
 </histogram>
 
 <histogram name="Shutdown.OtherExit.Time2" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>etienneb@chromium.org</owner>
   <owner>gab@chromium.org</owner>
   <summary>
@@ -9604,7 +9604,7 @@
 </histogram>
 
 <histogram name="Tracing.ElevatedTracingService.LaunchResult.{Stage}"
-    enum="Hresult" expires_after="2026-02-01">
+    enum="Hresult" expires_after="2026-04-05">
   <owner>etiennep@chromium.org</owner>
   <owner>grt@chromium.org</owner>
   <summary>
@@ -9690,7 +9690,7 @@
 </histogram>
 
 <histogram name="UpdateClient.DownloadTime2{AppID}" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>noahrose@google.com</owner>
   <owner>sorin@chromium.org</owner>
   <owner>waffles@chromium.org</owner>
@@ -10076,7 +10076,7 @@
 </histogram>
 
 <histogram name="WebFont.CacheHit" enum="WebFontCacheHit"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hajimehoshi@chromium.org</owner>
   <owner>kenjibaheux@chromium.org</owner>
   <owner>kouhei@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/page/histograms.xml b/tools/metrics/histograms/metadata/page/histograms.xml
index 5a3a27d..04e7c53 100644
--- a/tools/metrics/histograms/metadata/page/histograms.xml
+++ b/tools/metrics/histograms/metadata/page/histograms.xml
@@ -289,7 +289,7 @@
 </variants>
 
 <histogram name="PageActionController.ActionTypeShown2"
-    enum="PageActionIconType" expires_after="2026-02-01">
+    enum="PageActionIconType" expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>koretadaniel@chromium.org</owner>
   <owner>top-chrome-desktop-ui@google.com</owner>
@@ -1398,7 +1398,7 @@
 
 <histogram
     name="PageLoad.Clients.GoogleSearch.NavigationTiming.ConnectedCallbackDelay2"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>hayato@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -1412,7 +1412,7 @@
 
 <histogram
     name="PageLoad.Clients.GoogleSearch.NavigationTiming.CreateStreamDelay2"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>hayato@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -2166,7 +2166,7 @@
 </histogram>
 
 <histogram name="PageLoad.Clients.LinkPreview.Usage" enum="LinkPreviewUsage"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>toyoshim@chromium.org</owner>
   <owner>src/chrome/browser/preloading/preview/OWNERS</owner>
   <summary>
@@ -2276,7 +2276,7 @@
 
 <histogram
     name="PageLoad.Clients.PerformanceManager.LCPToLoadedIdle{LoadedIdleVisibility}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>joenotcharles@google.com</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -2291,7 +2291,7 @@
 
 <histogram
     name="PageLoad.Clients.PerformanceManager.LCPWithoutLoadedIdle{LoadedIdleVisibility}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>joenotcharles@google.com</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -2326,7 +2326,7 @@
 
 <histogram
     name="PageLoad.Clients.PerformanceManager.NavigationToLoadedIdle{LoadedIdleVisibility}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>joenotcharles@google.com</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -2342,7 +2342,7 @@
 
 <histogram
     name="PageLoad.Clients.PerformanceManager.NavigationWithoutLoadedIdle{LoadedIdleVisibility}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>joenotcharles@google.com</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -2530,7 +2530,7 @@
 
 <histogram
     name="PageLoad.Clients.ServiceWorker2.PaintTiming.{Timing}.RaceNetworkRequestEligible"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>sisidovski@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -2549,7 +2549,7 @@
 
 <histogram
     name="PageLoad.Clients.ServiceWorker2.PaintTiming.{Timing}.SyntheticResponse"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>sisidovski@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -2591,7 +2591,7 @@
 
 <histogram
     name="PageLoad.Clients.ServiceWorker2.ParseTiming.NavigationToParseStart{SyntheticResponseSuffix}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>sisidovski@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -3100,7 +3100,7 @@
 </histogram>
 
 <histogram name="PageLoad.DocumentTiming.NavigationToLoadEventFired" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -4484,7 +4484,7 @@
 </histogram>
 
 <histogram name="PageLoad.PaintTiming.NavigationToFirstPaint" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>sullivan@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -4498,7 +4498,7 @@
 
 <histogram
     name="PageLoad.PaintTiming.NavigationToFirstPaint.AfterBackForwardCacheRestore"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>djw@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
@@ -4786,7 +4786,7 @@
 </histogram>
 
 <histogram name="PageLoad.ParseTiming.NavigationToParseStart" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <improvement direction="LOWER_IS_BETTER"/>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml
index 3853a80..d565594 100644
--- a/tools/metrics/histograms/metadata/password/histograms.xml
+++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -661,7 +661,7 @@
 </histogram>
 
 <histogram name="PasswordManager.AffiliationBackend.FetchSize" units="facets"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>vsemeniuk@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -846,7 +846,7 @@
 </histogram>
 
 <histogram name="PasswordManager.AuthenticationStateWin"
-    enum="AuthenticationStateWin" expires_after="2026-02-01">
+    enum="AuthenticationStateWin" expires_after="2026-04-05">
   <owner>sygiet@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -966,7 +966,7 @@
 </histogram>
 
 <histogram name="PasswordManager.BubbleSuppression.AccountsInStatisticsTable2"
-    units="accounts" expires_after="2026-02-01">
+    units="accounts" expires_after="2026-04-05">
   <owner>kazinova@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -1169,7 +1169,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ChangePasswordFormDetected" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>atsvirchkova@google.com</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>
@@ -1180,7 +1180,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ChangePasswordFormDetectionTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>atsvirchkova@google.com</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>
@@ -1191,7 +1191,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ChangingPasswordToast.TimeSpent" units="ms"
-    expires_after="2025-11-30">
+    expires_after="2026-04-05">
   <owner>rgod@google.com</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>
@@ -1645,7 +1645,7 @@
 
 <histogram
     name="PasswordManager.FillingAssistance{PasswordAccountStorageUsageLevelOrSecurityOrigin}"
-    enum="PasswordManagerFillingAssistance" expires_after="2026-02-01">
+    enum="PasswordManagerFillingAssistance" expires_after="2026-04-05">
   <owner>kazinova@google.com</owner>
   <owner>battre@chromium.org</owner>
   <summary>
@@ -1747,7 +1747,7 @@
 </histogram>
 
 <histogram name="PasswordManager.FinalPasswordChangeStatus"
-    enum="PasswordChangeFlowState" expires_after="2026-02-01">
+    enum="PasswordChangeFlowState" expires_after="2026-04-05">
   <owner>vsemeniuk@google.com</owner>
   <owner>atsvirchkova@google.com</owner>
   <summary>
@@ -1819,7 +1819,7 @@
 </histogram>
 
 <histogram name="PasswordManager.HasPasswordChangeUrl" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>atsvirchkova@google.com</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>
@@ -1895,7 +1895,7 @@
 </histogram>
 
 <histogram name="PasswordManager.Import.PerFile.Notes.{Type}" units="units"
-    expires_after="2025-12-11">
+    expires_after="2026-04-05">
   <owner>natiahlyi@google.com</owner>
   <owner>markusheintz@google.com</owner>
   <summary>
@@ -2282,7 +2282,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LogInWithPasswordChangeSubmission"
-    enum="LogInWithChangedPasswordOutcome" expires_after="2026-02-04">
+    enum="LogInWithChangedPasswordOutcome" expires_after="2026-04-05">
   <owner>fiorellab@google.com</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>
@@ -2379,7 +2379,7 @@
 </histogram>
 
 <histogram name="PasswordManager.MediationOptional"
-    enum="CredentialManagerGetResult" expires_after="2026-02-01">
+    enum="CredentialManagerGetResult" expires_after="2026-04-05">
   <owner>kazinova@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -2612,7 +2612,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordChange.ChangingPasswordToast"
-    enum="PasswordChangeToastEvent" expires_after="2026-02-01">
+    enum="PasswordChangeToastEvent" expires_after="2026-04-05">
   <owner>rgod@google.com</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>
@@ -2680,7 +2680,7 @@
 
 <histogram
     name="PasswordManager.PasswordChange.LeakDetectionDialog.TimeSpent{DialogType}"
-    units="ms" expires_after="2025-11-30">
+    units="ms" expires_after="2026-04-05">
   <owner>rgod@google.com</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>
@@ -2697,7 +2697,7 @@
 
 <histogram
     name="PasswordManager.PasswordChange.LeakDetectionDialog{DialogType}"
-    enum="PasswordChangeDialogAction" expires_after="2026-02-01">
+    enum="PasswordChangeDialogAction" expires_after="2026-04-05">
   <owner>rgod@google.com</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>
@@ -2805,7 +2805,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordChangeAvailability"
-    enum="PasswordChangeAvailability" expires_after="2026-02-01">
+    enum="PasswordChangeAvailability" expires_after="2026-04-05">
   <owner>vsemeniuk@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -2838,7 +2838,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordChangeTimeOverall" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>atsvirchkova@google.com</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>
@@ -3889,7 +3889,7 @@
 
 <histogram
     name="PasswordManager.SavedPasswordIsGenerated{PasswordAccountStorageUsageLevel}"
-    enum="BooleanSavedPasswordIsGenerated" expires_after="2026-02-01">
+    enum="BooleanSavedPasswordIsGenerated" expires_after="2026-04-05">
   <owner>kazinova@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -4032,7 +4032,7 @@
 </histogram>
 
 <histogram name="PasswordManager.StoresUsedForFillingInLast28Days"
-    enum="PasswordManagerFillingSource" expires_after="2026-02-01">
+    enum="PasswordManagerFillingSource" expires_after="2026-04-05">
   <owner>mamir@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -4044,7 +4044,7 @@
 </histogram>
 
 <histogram name="PasswordManager.StoresUsedForFillingInLast7Days"
-    enum="PasswordManagerFillingSource" expires_after="2026-02-01">
+    enum="PasswordManagerFillingSource" expires_after="2026-04-05">
   <owner>mamir@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -4461,7 +4461,7 @@
 </histogram>
 
 <histogram name="PasswordManager.{Function}.TimeSinceInit" units="units"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>vsemeniuk@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -4620,7 +4620,7 @@
 </histogram>
 
 <histogram name="PasswordManager.{Store}ReencryptedWithAsyncOSCrypt"
-    enum="BooleanOccurred" expires_after="2026-02-01">
+    enum="BooleanOccurred" expires_after="2026-04-05">
   <owner>vasilii@chromium.org</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/payment/histograms.xml b/tools/metrics/histograms/metadata/payment/histograms.xml
index 7e077ab..4995350 100644
--- a/tools/metrics/histograms/metadata/payment/histograms.xml
+++ b/tools/metrics/histograms/metadata/payment/histograms.xml
@@ -23,7 +23,7 @@
 <histograms>
 
 <histogram name="PaymentRequest.AddressEditorTrigerred"
-    enum="PaymentRequestAddressEditorMode" expires_after="2026-01-18">
+    enum="PaymentRequestAddressEditorMode" expires_after="2026-04-05">
   <owner>vykochko@chromium.org</owner>
   <owner>smcgruer@google.com</owner>
   <owner>payments-autofill-team@google.com</owner>
@@ -61,7 +61,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.Events2" units="bitfield value"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>smcgruer@chromium.org</owner>
   <owner>chrome-payments-eng@google.com</owner>
   <summary>
@@ -265,7 +265,7 @@
 <histogram
     name="PaymentRequest.SecurePaymentConfirmation.BrowserBoundKeyStoreCreate"
     enum="SecurePaymentConfirmationBrowserBoundKeyDeviceResult"
-    expires_after="2025-12-04">
+    expires_after="2026-04-05">
   <owner>slobodan@chromium.org</owner>
   <owner>chrome-payments-eng@google.com</owner>
   <summary>
@@ -289,7 +289,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.Show.TryShowOutcome"
-    enum="PaymentRequestTryShowOutcome" expires_after="2026-02-01">
+    enum="PaymentRequestTryShowOutcome" expires_after="2026-04-05">
   <owner>smcgruer@chromium.org</owner>
   <owner>chrome-payments-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/pdf/histograms.xml b/tools/metrics/histograms/metadata/pdf/histograms.xml
index f122ec4..a44e227 100644
--- a/tools/metrics/histograms/metadata/pdf/histograms.xml
+++ b/tools/metrics/histograms/metadata/pdf/histograms.xml
@@ -78,7 +78,7 @@
   </summary>
 </histogram>
 
-<histogram name="PDF.HasAttachment" enum="Boolean" expires_after="2026-02-01">
+<histogram name="PDF.HasAttachment" enum="Boolean" expires_after="2026-04-05">
   <owner>nigi@chromium.org</owner>
   <owner>thestig@chromium.org</owner>
   <summary>
@@ -104,7 +104,7 @@
 </histogram>
 
 <histogram name="PDF.Ink2StrokeBrushType" enum="PDFInk2StrokeBrushType"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>andyphan@chromium.org</owner>
   <owner>thestig@chromium.org</owner>
   <summary>
@@ -118,7 +118,7 @@
 </histogram>
 
 <histogram name="PDF.Ink2StrokeHighlighterColor"
-    enum="PDFInk2StrokeHighlighterColor" expires_after="2026-02-01">
+    enum="PDFInk2StrokeHighlighterColor" expires_after="2026-04-05">
   <owner>andyphan@chromium.org</owner>
   <owner>thestig@chromium.org</owner>
   <summary>
@@ -130,7 +130,7 @@
 </histogram>
 
 <histogram name="PDF.Ink2StrokeInputDeviceType"
-    enum="PDFInk2StrokeInputDeviceType" expires_after="2026-02-01">
+    enum="PDFInk2StrokeInputDeviceType" expires_after="2026-04-05">
   <owner>andyphan@chromium.org</owner>
   <owner>thestig@chromium.org</owner>
   <summary>
@@ -145,7 +145,7 @@
 </histogram>
 
 <histogram name="PDF.Ink2StrokePenColor" enum="PDFInk2StrokePenColor"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>andyphan@chromium.org</owner>
   <owner>thestig@chromium.org</owner>
   <summary>
@@ -156,7 +156,7 @@
 </histogram>
 
 <histogram name="PDF.Ink2Stroke{Brush}Size" enum="PDFInk2StrokeBrushSize"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>andyphan@chromium.org</owner>
   <owner>thestig@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/performance_manager/histograms.xml b/tools/metrics/histograms/metadata/performance_manager/histograms.xml
index 4220cfa4..6665493 100644
--- a/tools/metrics/histograms/metadata/performance_manager/histograms.xml
+++ b/tools/metrics/histograms/metadata/performance_manager/histograms.xml
@@ -106,7 +106,7 @@
 
 <histogram
     name="CPU.Experimental.EstimatedFrequencyAsPercentOfLimit.{CoreType}"
-    units="%" expires_after="2026-02-01">
+    units="%" expires_after="2026-04-05">
   <owner>anthonyvd@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -297,7 +297,7 @@
 
 <histogram
     name="PerformanceManager.RegistryStats.FrontEndHeapDebugOptionsOutcome"
-    enum="FrontEndHeapDebugOptionsOutcome" expires_after="2026-02-01">
+    enum="FrontEndHeapDebugOptionsOutcome" expires_after="2026-04-05">
   <owner>fdoray@chromium.org</owner>
   <owner>chrome-catan@google.com</owner>
   <summary>
@@ -373,7 +373,7 @@
 </histogram>
 
 <histogram name="PerformanceManager.UserTuning.EfficiencyMode"
-    enum="EfficiencyMode" expires_after="2026-02-01">
+    enum="EfficiencyMode" expires_after="2026-04-05">
   <owner>anthonyvd@chromium.org</owner>
   <owner>chrome-catan@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/permissions/histograms.xml b/tools/metrics/histograms/metadata/permissions/histograms.xml
index be3fc3a..33d6f68 100644
--- a/tools/metrics/histograms/metadata/permissions/histograms.xml
+++ b/tools/metrics/histograms/metadata/permissions/histograms.xml
@@ -475,7 +475,7 @@
 </histogram>
 
 <histogram name="Permissions.AIv1.SessionCreationSuccess" enum="BooleanSuccess"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>elklm@chromium.org</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
   <summary>
@@ -486,7 +486,7 @@
 </histogram>
 
 <histogram name="Permissions.AIv4.ComputeEmbeddingsDuration" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>elklm@chromium.org</owner>
   <owner>hempjudith@google.com</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
@@ -500,7 +500,7 @@
 </histogram>
 
 <histogram name="Permissions.AIv4.ComputeEmbeddingsStatus"
-    enum="ComputeEmbeddingsStatus" expires_after="2026-02-01">
+    enum="ComputeEmbeddingsStatus" expires_after="2026-04-05">
   <owner>elklm@chromium.org</owner>
   <owner>hempjudith@google.com</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
@@ -513,7 +513,7 @@
 </histogram>
 
 <histogram name="Permissions.AIv4.EmbedderMetadataValid" enum="BooleanValid"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>elklm@chromium.org</owner>
   <owner>hempjudith@google.com</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
@@ -525,7 +525,7 @@
 </histogram>
 
 <histogram name="Permissions.AIv4.FinishedPassageEmbeddingsTaskOutdated"
-    enum="BooleanSuccess" expires_after="2026-02-01">
+    enum="BooleanSuccess" expires_after="2026-04-05">
   <owner>elklm@chromium.org</owner>
   <owner>hempjudith@google.com</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
@@ -539,7 +539,7 @@
 </histogram>
 
 <histogram name="Permissions.AIv4.LanguageDetectionStatus"
-    enum="LanguageDetectionStatus" expires_after="2026-02-01">
+    enum="LanguageDetectionStatus" expires_after="2026-04-05">
   <owner>elklm@chromium.org</owner>
   <owner>hempjudith@google.com</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
@@ -551,7 +551,7 @@
 </histogram>
 
 <histogram name="Permissions.AIv4.PassageEmbeddingsComputationTimeout"
-    enum="Boolean" expires_after="2026-02-01">
+    enum="Boolean" expires_after="2026-04-05">
   <owner>elklm@chromium.org</owner>
   <owner>hempjudith@google.com</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
@@ -564,7 +564,7 @@
 </histogram>
 
 <histogram name="Permissions.AIv4.TryCancelPreviousEmbeddingsModelExecution"
-    enum="BooleanSuccess" expires_after="2026-02-01">
+    enum="BooleanSuccess" expires_after="2026-04-05">
   <owner>elklm@chromium.org</owner>
   <owner>hempjudith@google.com</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
@@ -779,7 +779,7 @@
 </histogram>
 
 <histogram name="Permissions.Engagement.Accepted{PermissionRequestTypes}"
-    units="%" expires_after="2026-02-01">
+    units="%" expires_after="2026-04-05">
   <owner>engedy@chromium.org</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
   <summary>
@@ -1881,7 +1881,7 @@
 </histogram>
 
 <histogram name="Permissions.SiteSettingsChanged" enum="ContentType"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>lyf@chromium.org</owner>
   <owner>elklm@chromium.org</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
@@ -2037,7 +2037,7 @@
 </histogram>
 
 <histogram name="Permissions.{PredictionModel}.SnapshotTaken"
-    enum="BooleanSuccess" expires_after="2026-02-01">
+    enum="BooleanSuccess" expires_after="2026-04-05">
   <owner>elklm@chromium.org</owner>
   <owner>hempjudith@google.com</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
@@ -2050,7 +2050,7 @@
 </histogram>
 
 <histogram name="Permissions.{PredictionModel}.SnapshotTakenDuration"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>elklm@chromium.org</owner>
   <owner>hempjudith@google.com</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
@@ -2079,7 +2079,7 @@
 
 <histogram
     name="Permissions.{PredictionModel}.{PermissionType}.RenderedTextSize"
-    units="characters" expires_after="2026-02-01">
+    units="characters" expires_after="2026-04-05">
   <owner>elklm@chromium.org</owner>
   <owner>hempjudith@google.com</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
@@ -2161,7 +2161,7 @@
 </histogram>
 
 <histogram name="WebsiteSettings.AllSitesAction2"
-    enum="WebSiteSettingsAllSitesAction2" expires_after="2026-02-01">
+    enum="WebSiteSettingsAllSitesAction2" expires_after="2026-04-05">
   <owner>sauski@google.com</owner>
   <owner>alimariam@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/platform/histograms.xml b/tools/metrics/histograms/metadata/platform/histograms.xml
index 441fcfa..94acc51 100644
--- a/tools/metrics/histograms/metadata/platform/histograms.xml
+++ b/tools/metrics/histograms/metadata/platform/histograms.xml
@@ -122,7 +122,7 @@
 </histogram>
 
 <histogram name="Platform.Chaps.ReinitializingToken"
-    enum="ChapsReinitializingToken" expires_after="2026-02-01">
+    enum="ChapsReinitializingToken" expires_after="2026-04-05">
   <owner>chenyian@google.com</owner>
   <owner>cros-hwsec-userland-eng+uma@google.com</owner>
   <summary>
@@ -155,7 +155,7 @@
 </histogram>
 
 <histogram name="Platform.Chaps.TokenManager.LoadToken"
-    enum="TokenManagerStatus" expires_after="2026-02-01">
+    enum="TokenManagerStatus" expires_after="2026-04-05">
   <owner>chenyian@google.com</owner>
   <owner>cros-hwsec-userland-eng+uma@google.com</owner>
   <summary>
@@ -177,7 +177,7 @@
 </histogram>
 
 <histogram name="Platform.Chaps.TPMAvailability" enum="ChapsTPMAvailability"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>chenyian@google.com</owner>
   <owner>cros-hwsec-userland-eng+uma@google.com</owner>
   <summary>Each sample is the TPM Availability status.</summary>
@@ -1259,7 +1259,7 @@
 </histogram>
 
 <histogram name="Platform.Libhwsec.PinWeaverManager.SyncHashTree.SyncOutcome"
-    enum="HwsecPinWeaverSyncOutcomeEnum" expires_after="2026-02-01">
+    enum="HwsecPinWeaverSyncOutcomeEnum" expires_after="2026-04-05">
   <owner>chensa@google.com</owner>
   <owner>cros-hwsec-userland-eng+uma@google.com</owner>
   <summary>
@@ -1863,7 +1863,7 @@
 </histogram>
 
 <histogram name="Platform.Missive.ResourceExhaustedCase"
-    enum="ResourceExhaustedCase" expires_after="2026-02-01">
+    enum="ResourceExhaustedCase" expires_after="2026-04-05">
   <owner>djzhang@google.com</owner>
   <owner>jdudder@google.com</owner>
   <owner>cros-reporting-team@google.com</owner>
@@ -1875,7 +1875,7 @@
 </histogram>
 
 <histogram name="Platform.Missive.StartStatus"
-    enum="EnterpriseCloudReportingStatusCode" expires_after="2026-02-01">
+    enum="EnterpriseCloudReportingStatusCode" expires_after="2026-04-05">
   <owner>djzhang@google.com</owner>
   <owner>jdudder@google.com</owner>
   <owner>cros-reporting-team@google.com</owner>
@@ -1984,7 +1984,7 @@
 </histogram>
 
 <histogram name="Platform.Modemfwd.FWInstallTime" units="seconds"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ujjwalpande@google.com</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
@@ -2008,7 +2008,7 @@
 </histogram>
 
 <histogram name="Platform.Modemfwd.ModemRecoveryState"
-    enum="ModemfwdModemRecoveryState" expires_after="2026-02-01">
+    enum="ModemfwdModemRecoveryState" expires_after="2026-04-05">
   <owner>ujjwalpande@google.com</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>Report state of modem recovery operation.</summary>
@@ -2624,7 +2624,7 @@
 </histogram>
 
 <histogram name="Platform.TPM.DictionaryAttackResetStatus"
-    enum="CrosTPMDictionaryAttackResetStatusEnum" expires_after="2026-02-01">
+    enum="CrosTPMDictionaryAttackResetStatusEnum" expires_after="2026-04-05">
   <owner>yich@google.com</owner>
   <owner>cros-hwsec-userland-eng+uma@google.com</owner>
   <summary>
@@ -2707,7 +2707,7 @@
 </histogram>
 
 <histogram name="Platform.TPM.PowerWashResult" enum="TPMPowerWashResult"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>chenyian@google.com</owner>
   <owner>cros-hwsec-userland-eng+uma@google.com</owner>
   <summary>
@@ -2843,7 +2843,7 @@
 </histogram>
 
 <histogram name="Platform.Trunks.FirstTimeoutWritingCommand"
-    enum="TPMCommandCode" expires_after="2026-02-01">
+    enum="TPMCommandCode" expires_after="2026-04-05">
   <owner>chingkang@chromium.org</owner>
   <owner>cros-hwsec+uma@google.com</owner>
   <summary>Command code of the first timeout writing TPM command</summary>
diff --git a/tools/metrics/histograms/metadata/power/histograms.xml b/tools/metrics/histograms/metadata/power/histograms.xml
index be64b413..7969b3bf 100644
--- a/tools/metrics/histograms/metadata/power/histograms.xml
+++ b/tools/metrics/histograms/metadata/power/histograms.xml
@@ -1794,7 +1794,7 @@
 </histogram>
 
 <histogram name="Power.SuspendAttempt" enum="SuspendAttempt"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>puthik@chromium.org</owner>
   <owner>chromeos-platform-power@google.com</owner>
   <summary>
@@ -2026,7 +2026,7 @@
 </histogram>
 
 <histogram name="PowerML.SmartDimModel.RequestCanceledDuration" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>alanlxl@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>napper@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/prefetch/histograms.xml b/tools/metrics/histograms/metadata/prefetch/histograms.xml
index 8bd12bfb..669f1499b 100644
--- a/tools/metrics/histograms/metadata/prefetch/histograms.xml
+++ b/tools/metrics/histograms/metadata/prefetch/histograms.xml
@@ -135,7 +135,7 @@
 
 <histogram
     name="Prefetch.PrefetchContainer.AddedToHeaderDeterminedSuccessfully.{TriggerTypeAndEagerness}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>kenoss@chromium.org</owner>
   <owner>chrome-loading@chromium.org</owner>
   <summary>
@@ -152,7 +152,7 @@
 
 <histogram
     name="Prefetch.PrefetchContainer.AddedToInitialEligibility.{TriggerTypeAndEagerness}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>kenoss@chromium.org</owner>
   <owner>chrome-loading@chromium.org</owner>
   <summary>
@@ -169,7 +169,7 @@
 
 <histogram
     name="Prefetch.PrefetchContainer.AddedToPrefetchCompletedSuccessfully.{TriggerTypeAndEagerness}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>kenoss@chromium.org</owner>
   <owner>chrome-loading@chromium.org</owner>
   <summary>
@@ -186,7 +186,7 @@
 
 <histogram
     name="Prefetch.PrefetchContainer.AddedToPrefetchStarted.{TriggerTypeAndEagerness}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>kenoss@chromium.org</owner>
   <owner>chrome-loading@chromium.org</owner>
   <summary>
@@ -204,7 +204,7 @@
 
 <histogram
     name="Prefetch.PrefetchContainer.AddedToURLRequestStarted.{TriggerTypeAndEagerness}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>taiyo@chromium.org</owner>
   <owner>kenoss@chromium.org</owner>
   <owner>chrome-loading@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/printing/histograms.xml b/tools/metrics/histograms/metadata/printing/histograms.xml
index 1606f478..730852ab 100644
--- a/tools/metrics/histograms/metadata/printing/histograms.xml
+++ b/tools/metrics/histograms/metadata/printing/histograms.xml
@@ -371,7 +371,7 @@
 </histogram>
 
 <histogram name="Printing.CUPS.ProtocolUsed" enum="PrinterProtocol"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>bmgordon@chromium.org</owner>
   <owner>cros-printing-dev@chromium.org</owner>
   <summary>
@@ -522,7 +522,7 @@
 </histogram>
 
 <histogram name="PrintPreview.EnumeratePrintersTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>awscreen@chromium.org</owner>
   <owner>thestig@chromium.org</owner>
   <summary>
@@ -568,7 +568,7 @@
 </histogram>
 
 <histogram name="PrintPreview.InitialDisplayTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>thestig@chromium.org</owner>
   <owner>dhoss@chromium.org</owner>
   <summary>
@@ -676,7 +676,7 @@
 </histogram>
 
 <histogram name="PrintPreview.PrinterStatus.{StatusReason}.PrintJobSuccess"
-    enum="BooleanSuccess" expires_after="2025-11-03">
+    enum="BooleanSuccess" expires_after="2026-11-03">
   <owner>gavinwill@google.com</owner>
   <owner>cros-device-enablement@google.com</owner>
   <summary>
@@ -795,7 +795,7 @@
 </histogram>
 
 <histogram name="PrintPreview.UserAction" enum="PrintPreviewUserActionType"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>thestig@chromium.org</owner>
   <owner>awscreen@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/privacy/histograms.xml b/tools/metrics/histograms/metadata/privacy/histograms.xml
index b8fc53f..5b4cd50 100644
--- a/tools/metrics/histograms/metadata/privacy/histograms.xml
+++ b/tools/metrics/histograms/metadata/privacy/histograms.xml
@@ -1325,7 +1325,7 @@
 
 <histogram name="PrivacySandbox.Attestations.LoadAPKAssetStatus"
     enum="PrivacySandboxAttestationLoadAPKAssetStatus"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>shivanisha@chromium.org</owner>
   <owner>xiaochenzh@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/profile/histograms.xml b/tools/metrics/histograms/metadata/profile/histograms.xml
index 24a7226..0bf6565 100644
--- a/tools/metrics/histograms/metadata/profile/histograms.xml
+++ b/tools/metrics/histograms/metadata/profile/histograms.xml
@@ -791,7 +791,7 @@
 </histogram>
 
 <histogram name="ProfileChooser.HasProfilesShown" enum="BooleanShown"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>vasilii@chromium.org</owner>
   <owner>ewald@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/quick_answers/histograms.xml b/tools/metrics/histograms/metadata/quick_answers/histograms.xml
index 9e57fd0..e0aacb3 100644
--- a/tools/metrics/histograms/metadata/quick_answers/histograms.xml
+++ b/tools/metrics/histograms/metadata/quick_answers/histograms.xml
@@ -37,7 +37,7 @@
 </variants>
 
 <histogram name="QuickAnswers.ActiveImpression" enum="QuickAnswersResultType"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>yawano@google.com</owner>
   <owner>assistive-eng@google.com</owner>
   <summary>
@@ -62,7 +62,7 @@
 </histogram>
 
 <histogram name="QuickAnswers.Click" enum="QuickAnswersResultType"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>yawano@google.com</owner>
   <owner>assistive-eng@google.com</owner>
   <summary>
@@ -150,7 +150,7 @@
 </histogram>
 
 <histogram name="QuickAnswers.FeatureEnabled" enum="BooleanEnabled"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>yawano@google.com</owner>
   <owner>assistive-eng@google.com</owner>
   <summary>
@@ -161,7 +161,7 @@
 </histogram>
 
 <histogram name="QuickAnswers.Intent" enum="QuickAnswersIntentType"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>yawano@google.com</owner>
   <owner>assistive-eng@google.com</owner>
   <summary>
@@ -172,7 +172,7 @@
 </histogram>
 
 <histogram name="QuickAnswers.Loading.Duration" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>yawano@google.com</owner>
   <owner>assistive-eng@google.com</owner>
   <summary>
@@ -286,7 +286,7 @@
 </histogram>
 
 <histogram name="QuickAnswers.V2.Consent" units="impressions"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>yawano@google.com</owner>
   <owner>assistive-eng@google.com</owner>
   <summary>
@@ -313,7 +313,7 @@
 
 <histogram
     name="QuickAnswers.V2.Consent.Impression{QuickAnswersV2ConsentResultType}"
-    units="impressions" expires_after="2026-02-01">
+    units="impressions" expires_after="2026-04-05">
   <owner>yawano@google.com</owner>
   <owner>assistive-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/quota/histograms.xml b/tools/metrics/histograms/metadata/quota/histograms.xml
index 285e6f6..8b1ee6e 100644
--- a/tools/metrics/histograms/metadata/quota/histograms.xml
+++ b/tools/metrics/histograms/metadata/quota/histograms.xml
@@ -367,7 +367,7 @@
 </histogram>
 
 <histogram name="Quota.TotalDiskSpaceIsZero" enum="BooleanVolumeZero"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ayui@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/regional_capabilities/histograms.xml b/tools/metrics/histograms/metadata/regional_capabilities/histograms.xml
index de53f8a..58e283e 100644
--- a/tools/metrics/histograms/metadata/regional_capabilities/histograms.xml
+++ b/tools/metrics/histograms/metadata/regional_capabilities/histograms.xml
@@ -23,7 +23,7 @@
 <histograms>
 
 <histogram name="RegionalCapabilities.ActiveRegionalProgram2"
-    enum="ActiveRegionalProgram" expires_after="2026-01-31">
+    enum="ActiveRegionalProgram" expires_after="2026-04-05">
   <owner>ljjlee@google.com</owner>
   <owner>chrome-regionalcapabilities@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/renderer4/histograms.xml b/tools/metrics/histograms/metadata/renderer4/histograms.xml
index e08f972..dbf7349c 100644
--- a/tools/metrics/histograms/metadata/renderer4/histograms.xml
+++ b/tools/metrics/histograms/metadata/renderer4/histograms.xml
@@ -128,7 +128,7 @@
 </histogram>
 
 <histogram name="Renderer4.GpuImageUploadState.FirstLockWasted"
-    enum="BooleanWasted" expires_after="2026-02-01">
+    enum="BooleanWasted" expires_after="2026-04-05">
   <owner>vmpstr@chromium.org</owner>
   <owner>thiabaud@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/enums.xml b/tools/metrics/histograms/metadata/safe_browsing/enums.xml
index 33fdc5a3..3e27f8a 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/enums.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/enums.xml
@@ -210,6 +210,11 @@
 <enum name="NotificationRevocationSource">
   <int value="0" label="Social engineering blocklist"/>
   <int value="1" label="Manual safe browsing revocation"/>
+  <int value="2" label="Standard one tap unsubscribe"/>
+  <int value="3" label="One tap unsubscribe after suspicious content warning"/>
+  <int value="4" label="Disruptive auto-revocation"/>
+  <int value="5" label="User manually changed site setting"/>
+  <int value="6" label="Unknown"/>
 </enum>
 
 <!-- LINT.ThenChange(//components/safe_browsing/core/browser/safe_browsing_metrics_collector.h:NotificationRevocationSource) -->
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index 9c8a902..2f97070 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -134,7 +134,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.Android.AdvancedProtection.Enabled"
-    enum="BooleanEnabled" expires_after="2026-02-01">
+    enum="BooleanEnabled" expires_after="2026-04-05">
   <owner>pkotwicz@google.com</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -460,7 +460,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.CheckUrl.Action"
-    enum="SafeBrowsingCheckUrlAction" expires_after="2026-02-01">
+    enum="SafeBrowsingCheckUrlAction" expires_after="2026-04-05">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -745,7 +745,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.Desktop.AdvancedProtection.Enabled"
-    enum="BooleanEnabled" expires_after="2026-02-01">
+    enum="BooleanEnabled" expires_after="2026-04-05">
   <owner>pkotwicz@google.com</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1489,7 +1489,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.GmsSafeBrowsingApi.HasUnknownExceptionType"
-    enum="BooleanSafeBrowsingApiUnknownException" expires_after="2026-02-01">
+    enum="BooleanSafeBrowsingApiUnknownException" expires_after="2026-04-05">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -2069,7 +2069,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.LocalBinaryUploadRequest.DlpResult"
-    enum="BooleanSuccess" expires_after="2025-11-20">
+    enum="BooleanSuccess" expires_after="2026-11-20">
   <owner>domfc@chromium.org</owner>
   <owner>nancylanxiao@google.com</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
@@ -2081,7 +2081,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.LocalBinaryUploadRequest.Duration" units="ms"
-    expires_after="2025-11-20">
+    expires_after="2026-11-20">
   <owner>domfc@chromium.org</owner>
   <owner>nancylanxiao@google.com</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
@@ -2093,7 +2093,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.LocalBinaryUploadRequest.Result"
-    enum="BooleanSuccess" expires_after="2025-11-20">
+    enum="BooleanSuccess" expires_after="2026-11-20">
   <owner>domfc@chromium.org</owner>
   <owner>nancylanxiao@google.com</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
@@ -2409,7 +2409,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.Pref.Enhanced.RegularProfile"
-    enum="BooleanEnabled" expires_after="2026-02-01">
+    enum="BooleanEnabled" expires_after="2026-04-05">
   <owner>zackhan@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -2477,7 +2477,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.ReferrerURLChainSize{ReferrerAttribution}"
-    units="units" expires_after="2026-02-01">
+    units="units" expires_after="2026-04-05">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -2630,7 +2630,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.EnterpriseRealTimePolicyEnabled.HasDmToken"
-    enum="BooleanHasToken" expires_after="2026-02-01">
+    enum="BooleanHasToken" expires_after="2026-04-05">
   <owner>thefrog@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -3027,6 +3027,9 @@
     <variant name="TailoredSecurity"
         summary="The user enters the Safe Browsing settings page from the UX
                  shown due to a change in the Tailored Security setting"/>
+    <variant name="TipsNotificationsPromo"
+        summary="The user enters the Safe Browsing settings page from the UX
+                 shown due to interacting with the tips notifications promo."/>
   </token>
 </histogram>
 
@@ -3044,7 +3047,7 @@
 
 <histogram
     name="SafeBrowsing.SiteProtection.FamiliarityHeuristic.Engagement15OrVisitedBeforeTodayOrHighConfidence"
-    enum="BooleanMatched" expires_after="2026-02-01">
+    enum="BooleanMatched" expires_after="2026-04-05">
   <owner>pkotwicz@chromium.org</owner>
   <owner>chrome-counter-abuse-core@google.com</owner>
   <summary>
@@ -3069,7 +3072,7 @@
 
 <histogram
     name="SafeBrowsing.SiteProtection.FamiliarityMetricDataFetchDuration"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>pkotwicz@chromium.org</owner>
   <owner>chrome-counter-abuse-core@google.com</owner>
   <summary>
@@ -3158,7 +3161,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.SyncedEsbDialogEnabledMessageDismissReason"
-    enum="MessageDismissReason" expires_after="2026-02-01">
+    enum="MessageDismissReason" expires_after="2026-04-05">
   <owner>zackhan@chromium.org</owner>
   <owner>chrome-counter-abuse-core@google.com</owner>
   <summary>
@@ -3274,7 +3277,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.TailoredSecurityService.OAuthTokenErrorState"
-    enum="GoogleServiceAuthError" expires_after="2026-02-01">
+    enum="GoogleServiceAuthError" expires_after="2026-04-05">
   <owner>jacastro@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -3584,7 +3587,7 @@
 
 <histogram
     name="SafeBrowsing.V4LocalDatabaseManager.TimeSinceLastUpdateResponse"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>danieltwhite@google.com</owner>
   <owner>joemerramos@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
@@ -3884,7 +3887,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4StoreFileOpenError" enum="PlatformFileError"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -3895,7 +3898,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4StoreFileWriteError" enum="PlatformFileError"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sb_client/histograms.xml b/tools/metrics/histograms/metadata/sb_client/histograms.xml
index 439e97f..1d1e3d78 100644
--- a/tools/metrics/histograms/metadata/sb_client/histograms.xml
+++ b/tools/metrics/histograms/metadata/sb_client/histograms.xml
@@ -87,7 +87,7 @@
 
 <histogram name="SBClientDownload.Android.GetReferringAppInfo.Result"
     enum="SafeBrowsingAndroidGetReferringAppInfoResult"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>chlily@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -113,7 +113,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.CancelEphemeralWarning"
-    enum="CancelEphemeralWarningEvent" expires_after="2026-02-01">
+    enum="CancelEphemeralWarningEvent" expires_after="2026-04-05">
   <owner>chlily@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -600,7 +600,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.HasVisualFeaturesImage" enum="BooleanExists"
-    expires_after="2026-01-29">
+    expires_after="2026-04-05">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -785,7 +785,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.NetworkRequestDuration" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>andysjlim@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -832,7 +832,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.OnDeviceModelExecutionDuration" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>andysjlim@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -843,7 +843,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.OnDeviceModelExecutionSuccess"
-    enum="BooleanSuccess" expires_after="2026-02-01">
+    enum="BooleanSuccess" expires_after="2026-04-05">
   <owner>andysjlim@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -895,7 +895,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.OnDeviceModelResponseParseSuccess"
-    enum="BooleanSuccess" expires_after="2026-02-01">
+    enum="BooleanSuccess" expires_after="2026-04-05">
   <owner>andysjlim@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -942,7 +942,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.OnDeviceModelSessionCreationTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>andysjlim@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1070,7 +1070,7 @@
 
 <histogram name="SBClientPhishing.PreClassificationCheckResult{RequestType}"
     enum="SBClientDetectionPreClassificationCheckResult"
-    expires_after="2026-01-30">
+    expires_after="2026-04-05">
   <owner>andysjlim@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1197,7 +1197,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.VisualFeatureTime" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>andysjlim@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1238,7 +1238,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.VisualFeatureTime.TotalDuration" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>andysjlim@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/security/histograms.xml b/tools/metrics/histograms/metadata/security/histograms.xml
index ff89ed48..cb52206 100644
--- a/tools/metrics/histograms/metadata/security/histograms.xml
+++ b/tools/metrics/histograms/metadata/security/histograms.xml
@@ -441,7 +441,7 @@
 </histogram>
 
 <histogram name="Security.PageInfo.MerchantTrustEngagement.{Event}"
-    units="EngagementScore" expires_after="2026-01-30">
+    units="EngagementScore" expires_after="2026-04-05">
   <owner>tommasin@chromium.org</owner>
   <owner>privacy-on-the-web@google.com</owner>
   <summary>
@@ -456,7 +456,7 @@
 </histogram>
 
 <histogram name="Security.PageInfo.MerchantTrustInteraction.{SiteFamiliarity}"
-    enum="MerchantTrustInteraction" expires_after="2026-01-30">
+    enum="MerchantTrustInteraction" expires_after="2026-04-05">
   <owner>tommasin@chromium.org</owner>
   <owner>privacy-on-the-web@google.com</owner>
   <summary>
@@ -827,7 +827,7 @@
 </histogram>
 
 <histogram name="Security.SecurityLevel.OnComplete" enum="SecurityLevel"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -928,7 +928,7 @@
 </histogram>
 
 <histogram name="SiteIsolation.IsolatableSandboxedIframes" units="processes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>wjmaclean@chromium.org</owner>
   <owner>alexmos@chromium.org</owner>
   <owner>creis@chromium.org</owner>
@@ -947,7 +947,7 @@
 </histogram>
 
 <histogram name="SiteIsolation.IsolatableSandboxedIframes.UniqueOrigins"
-    units="processes" expires_after="2026-02-01">
+    units="processes" expires_after="2026-04-05">
   <owner>wjmaclean@chromium.org</owner>
   <owner>alexmos@chromium.org</owner>
   <owner>creis@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/service/histograms.xml b/tools/metrics/histograms/metadata/service/histograms.xml
index c880fbfb..4307ae8 100644
--- a/tools/metrics/histograms/metadata/service/histograms.xml
+++ b/tools/metrics/histograms/metadata/service/histograms.xml
@@ -490,7 +490,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.FetchEvent.{Resource}.FetchResponseFrom"
-    enum="ServiceWorkerFetchResponseFrom" expires_after="2026-02-01">
+    enum="ServiceWorkerFetchResponseFrom" expires_after="2026-04-05">
   <owner>sisidovski@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -1478,7 +1478,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.SharedWorker.ResourceFetch.FromBlob"
-    enum="Boolean" expires_after="2026-02-01">
+    enum="Boolean" expires_after="2026-04-05">
   <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -1491,7 +1491,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.SharedWorker.ResourceFetch.UseServiceWorker"
-    enum="Boolean" expires_after="2026-02-01">
+    enum="Boolean" expires_after="2026-04-05">
   <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -1503,7 +1503,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.SharedWorkerScript.IsBlob" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -1849,7 +1849,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.Storage.ReadInitialDataFromDB.Time" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>chikamune@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <improvement direction="LOWER_IS_BETTER"/>
diff --git a/tools/metrics/histograms/metadata/session/histograms.xml b/tools/metrics/histograms/metadata/session/histograms.xml
index e8e441ef..96203e4 100644
--- a/tools/metrics/histograms/metadata/session/histograms.xml
+++ b/tools/metrics/histograms/metadata/session/histograms.xml
@@ -1017,7 +1017,7 @@
 </histogram>
 
 <histogram name="Session.WebStates.LoadingTimeOnMainThread" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>fedegermi@google.com</owner>
   <owner>sdefresne@chromium.org</owner>
   <summary>
@@ -1125,7 +1125,7 @@
 </histogram>
 
 <histogram name="Session.WebStates.StorageFormat" enum="SessionStorageFormat"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>fedegermi@google.com</owner>
   <owner>sdefresne@chromium.org</owner>
   <summary>
@@ -1153,7 +1153,7 @@
 </histogram>
 
 <histogram name="Session.WebStates.StorageMigrationStatus"
-    enum="SessionStorageMigrationStatus" expires_after="2026-02-01">
+    enum="SessionStorageMigrationStatus" expires_after="2026-04-05">
   <owner>fedegermi@google.com</owner>
   <owner>sdefresne@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/settings/histograms.xml b/tools/metrics/histograms/metadata/settings/histograms.xml
index 55ea36f8..8a09c86 100644
--- a/tools/metrics/histograms/metadata/settings/histograms.xml
+++ b/tools/metrics/histograms/metadata/settings/histograms.xml
@@ -274,7 +274,7 @@
 </histogram>
 
 <histogram name="Settings.FingerprintingProtection.Enabled"
-    enum="BooleanEnabled" expires_after="2026-02-01">
+    enum="BooleanEnabled" expires_after="2026-04-05">
   <owner>fmacintosh@google.com</owner>
   <owner>koilos@google.com</owner>
   <summary>
@@ -1145,7 +1145,7 @@
 </histogram>
 
 <histogram name="Settings.SafetyHub.EntryPointImpression"
-    enum="SafetyHubEntryPoint" expires_after="2026-02-01">
+    enum="SafetyHubEntryPoint" expires_after="2026-04-05">
   <owner>sideyilmaz@chromium.org</owner>
   <owner>tov@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
@@ -1155,7 +1155,7 @@
 </histogram>
 
 <histogram name="Settings.SafetyHub.EntryPointInteraction"
-    enum="SafetyHubEntryPoint" expires_after="2026-02-01">
+    enum="SafetyHubEntryPoint" expires_after="2026-04-05">
   <owner>sideyilmaz@chromium.org</owner>
   <owner>tov@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
@@ -1198,7 +1198,7 @@
 </histogram>
 
 <histogram name="Settings.SafetyHub.Interaction" enum="SafetyHubSurfaces"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>sideyilmaz@chromium.org</owner>
   <owner>tov@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
@@ -1219,7 +1219,7 @@
 </histogram>
 
 <histogram name="Settings.SafetyHub.MenuNotificationClicked"
-    enum="SafetyHubModuleType" expires_after="2026-02-01">
+    enum="SafetyHubModuleType" expires_after="2026-04-05">
   <owner>sideyilmaz@chromium.org</owner>
   <owner>tov@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
@@ -1230,7 +1230,7 @@
 </histogram>
 
 <histogram name="Settings.SafetyHub.MenuNotificationImpression"
-    enum="SafetyHubModuleType" expires_after="2026-02-01">
+    enum="SafetyHubModuleType" expires_after="2026-04-05">
   <owner>sideyilmaz@chromium.org</owner>
   <owner>tov@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
@@ -1430,7 +1430,7 @@
 
 <histogram
     name="Settings.TrackedPreferenceChanged{TrackedPreferencesExternalValidators}"
-    enum="TrackedPreference" expires_after="2026-02-01">
+    enum="TrackedPreference" expires_after="2026-04-05">
 <!-- Note to owner: This histogram can be renewed 1 full year if it is still
 useful for Chirp alerts (go/crca-alerts). -->
 
@@ -1472,7 +1472,7 @@
 
 <histogram
     name="Settings.TrackedPreferenceCleared{TrackedPreferencesExternalValidators}"
-    enum="TrackedPreference" expires_after="2026-02-01">
+    enum="TrackedPreference" expires_after="2026-04-05">
   <owner>anunoy@chromium.org</owner>
   <owner>junhao.huang@microsoft.com</owner>
   <summary>
@@ -1491,7 +1491,7 @@
 
 <histogram
     name="Settings.TrackedPreferenceInitialized{TrackedPreferencesExternalValidators}"
-    enum="TrackedPreference" expires_after="2026-02-01">
+    enum="TrackedPreference" expires_after="2026-04-05">
   <owner>anunoy@chromium.org</owner>
   <owner>junhao.huang@microsoft.com</owner>
   <summary>
@@ -1512,7 +1512,7 @@
 
 <histogram
     name="Settings.TrackedPreferenceNullInitialized{TrackedPreferencesExternalValidators}"
-    enum="TrackedPreference" expires_after="2026-02-01">
+    enum="TrackedPreference" expires_after="2026-04-05">
   <owner>anunoy@chromium.org</owner>
   <owner>junhao.huang@microsoft.com</owner>
   <summary>
@@ -1530,7 +1530,7 @@
 </histogram>
 
 <histogram name="Settings.TrackedPreferenceReset" enum="TrackedPreference"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>anunoy@chromium.org</owner>
   <owner>junhao.huang@microsoft.com</owner>
   <summary>The id of a tracked preference which was reset by Chrome.</summary>
@@ -1571,7 +1571,7 @@
 
 <histogram
     name="Settings.TrackedPreferenceTrustedInitialized{TrackedPreferencesExternalValidators}"
-    enum="TrackedPreference" expires_after="2026-02-01">
+    enum="TrackedPreference" expires_after="2026-04-05">
   <owner>anunoy@chromium.org</owner>
   <owner>junhao.huang@microsoft.com</owner>
   <summary>
@@ -1613,7 +1613,7 @@
 
 <histogram
     name="Settings.TrackedPreferenceUnchanged{TrackedPreferencesExternalValidators}"
-    enum="TrackedPreference" expires_after="2026-02-01">
+    enum="TrackedPreference" expires_after="2026-04-05">
   <owner>anunoy@chromium.org</owner>
   <owner>junhao.huang@microsoft.com</owner>
   <summary>
@@ -1631,7 +1631,7 @@
 </histogram>
 
 <histogram name="Settings.TrackedPreferenceWantedReset"
-    enum="TrackedPreference" expires_after="2026-02-01">
+    enum="TrackedPreference" expires_after="2026-04-05">
   <owner>anunoy@chromium.org</owner>
   <owner>junhao.huang@microsoft.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/signin/histograms.xml b/tools/metrics/histograms/metadata/signin/histograms.xml
index 5c15e28..4e732b0 100644
--- a/tools/metrics/histograms/metadata/signin/histograms.xml
+++ b/tools/metrics/histograms/metadata/signin/histograms.xml
@@ -730,7 +730,7 @@
 </histogram>
 
 <histogram name="Signin.AndroidDeviceAccountsNumberWhenEnteringFRE"
-    units="count" expires_after="2026-02-01">
+    units="count" expires_after="2026-04-05">
   <owner>bsazonov@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
@@ -903,7 +903,7 @@
 
 <histogram name="Signin.BoundSessionCredentials.CookieRotationResult{Trigger}"
     enum="BoundSessionCredentialsCookieRotationResult"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>alexilin@chromium.org</owner>
   <owner>msalama@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
@@ -2261,7 +2261,7 @@
 </histogram>
 
 <histogram name="Signin.ListFamilyMembersRequest.OverallLatency" units="ms"
-    expires_after="2026-01-31">
+    expires_after="2026-04-05">
   <owner>tju@google.com</owner>
   <owner>chrome-kids-eng@google.com</owner>
   <summary>
@@ -2284,7 +2284,7 @@
 </histogram>
 
 <histogram name="Signin.ListFamilyMembersRequest.RetryCount" units="retries"
-    expires_after="2026-01-31">
+    expires_after="2026-04-05">
   <owner>tju@google.com</owner>
   <owner>chrome-kids-eng@google.com</owner>
   <summary>
@@ -3084,7 +3084,7 @@
 </histogram>
 
 <histogram name="Signin.SigninManager.SetPrimaryAccountSigninInStage"
-    enum="GaiaIntegrationState" expires_after="2026-02-01">
+    enum="GaiaIntegrationState" expires_after="2026-04-05">
   <owner>droger@chromium.org</owner>
   <owner>anthie@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
@@ -3098,7 +3098,7 @@
 </histogram>
 
 <histogram name="Signin.SigninManager.SigninAccessPoint"
-    enum="SigninAccessPoint" expires_after="2026-02-01">
+    enum="SigninAccessPoint" expires_after="2026-04-05">
   <owner>droger@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/simple/histograms.xml b/tools/metrics/histograms/metadata/simple/histograms.xml
index 76aa20a..d3d6031 100644
--- a/tools/metrics/histograms/metadata/simple/histograms.xml
+++ b/tools/metrics/histograms/metadata/simple/histograms.xml
@@ -42,7 +42,7 @@
 </histogram>
 
 <histogram name="SimpleCache.{CacheInstance}CacheSizeOnInit2" units="MB"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>jkarlin@chromium.org</owner>
   <owner>shivanisha@chromium.org</owner>
   <summary>
@@ -52,7 +52,7 @@
 </histogram>
 
 <histogram name="SimpleCache.{CacheInstance}ConsistencyResult"
-    enum="SimpleCacheConsistencyResult" expires_after="2026-02-01">
+    enum="SimpleCacheConsistencyResult" expires_after="2026-04-05">
   <owner>wanderview@chromium.org</owner>
   <owner>morlovich@chromium.org</owner>
   <summary>
@@ -141,7 +141,7 @@
 </histogram>
 
 <histogram name="SimpleCache.{CacheInstance}DiskOpenLatency" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>morlovich@chromium.org</owner>
   <owner>wanderview@chromium.org</owner>
   <summary>
@@ -173,7 +173,7 @@
 </histogram>
 
 <histogram name="SimpleCache.{CacheInstance}Eviction.EntryCount" units="units"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>morlovich@chromium.org</owner>
   <owner>wanderview@chromium.org</owner>
   <summary>
@@ -182,7 +182,7 @@
 </histogram>
 
 <histogram name="SimpleCache.{CacheInstance}Eviction.TimeToDone" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>morlovich@chromium.org</owner>
   <owner>wanderview@chromium.org</owner>
   <summary>Time spent completing an eviction. {CacheInstance}</summary>
@@ -215,7 +215,7 @@
 </histogram>
 
 <histogram name="SimpleCache.{CacheInstance}IndexFileStateOnLoad"
-    enum="SimpleIndexState" expires_after="2026-02-01">
+    enum="SimpleIndexState" expires_after="2026-04-05">
   <owner>morlovich@chromium.org</owner>
   <owner>wanderview@chromium.org</owner>
   <summary>
@@ -234,7 +234,7 @@
 </histogram>
 
 <histogram name="SimpleCache.{CacheInstance}IndexNumEntriesOnInit"
-    units="entries" expires_after="2026-02-01">
+    units="entries" expires_after="2026-04-05">
   <owner>jkarlin@chromium.org</owner>
   <owner>morlovich@chromium.org</owner>
   <summary>
@@ -254,7 +254,7 @@
 </histogram>
 
 <histogram name="SimpleCache.{CacheInstance}MaxCacheSizeOnInit2" units="MB"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>morlovich@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <summary>The size limit computed for the cache. {CacheInstance}</summary>
@@ -341,7 +341,7 @@
 </histogram>
 
 <histogram name="SimpleGeolocation.Provider.ClientId"
-    enum="SimpleGeolocationClientId" expires_after="2026-02-01">
+    enum="SimpleGeolocationClientId" expires_after="2026-04-05">
   <owner>alvinji@chromium.org</owner>
   <owner>deviceapi-team@google.com</owner>
   <summary>
@@ -364,7 +364,7 @@
 </histogram>
 
 <histogram name="SimpleGeolocation.Provider.TimezoneResolverRequestInterval"
-    units="hours" expires_after="2026-02-01">
+    units="hours" expires_after="2026-04-05">
   <owner>alvinji@chromium.org</owner>
   <owner>deviceapi-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sql/histograms.xml b/tools/metrics/histograms/metadata/sql/histograms.xml
index 785239a..8a264f4 100644
--- a/tools/metrics/histograms/metadata/sql/histograms.xml
+++ b/tools/metrics/histograms/metadata/sql/histograms.xml
@@ -88,7 +88,7 @@
 </variants>
 
 <histogram name="Sql.Database.Open.FailureReason.{DatabaseTag}"
-    enum="OpenDatabaseFailedReason" expires_after="2026-02-01">
+    enum="OpenDatabaseFailedReason" expires_after="2026-04-05">
   <owner>anthonyvd@chromium.org</owner>
   <owner>etienneb@chromium.org</owner>
   <summary>
@@ -99,7 +99,7 @@
 </histogram>
 
 <histogram name="Sql.Database.Open.FirstAttempt.Error.{DatabaseTag}"
-    enum="SqliteResultCode" expires_after="2026-02-01">
+    enum="SqliteResultCode" expires_after="2026-04-05">
   <owner>anthonyvd@chromium.org</owner>
   <owner>etienneb@chromium.org</owner>
   <summary>
@@ -110,7 +110,7 @@
 </histogram>
 
 <histogram name="Sql.Database.Open.SecondAttempt.Error.{DatabaseTag}"
-    enum="SqliteResultCode" expires_after="2026-02-01">
+    enum="SqliteResultCode" expires_after="2026-04-05">
   <owner>anthonyvd@chromium.org</owner>
   <owner>etienneb@chromium.org</owner>
   <summary>
@@ -184,7 +184,7 @@
 </histogram>
 
 <histogram name="Sql.Recovery.Result" enum="SqlRecoveryResult"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>etienneb@chromium.org</owner>
   <owner>chrome-catan@google.com</owner>
   <summary>
@@ -193,7 +193,7 @@
 </histogram>
 
 <histogram name="Sql.Recovery.Result.{DatabaseTag}" enum="SqlRecoveryResult"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>etienneb@chromium.org</owner>
   <owner>chrome-catan@google.com</owner>
   <summary>
@@ -204,7 +204,7 @@
 </histogram>
 
 <histogram name="Sql.Recovery.ResultCode" enum="SqliteLoggedResultCode"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>etienneb@chromium.org</owner>
   <owner>chrome-catan@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/stability/histograms.xml b/tools/metrics/histograms/metadata/stability/histograms.xml
index 05c0b30..ab3417a 100644
--- a/tools/metrics/histograms/metadata/stability/histograms.xml
+++ b/tools/metrics/histograms/metadata/stability/histograms.xml
@@ -142,7 +142,7 @@
 </histogram>
 
 <histogram name="Stability.Android.KillSpareRendererAvailability.{KillType}"
-    enum="SpareRendererAvailabilityWhenKilled" expires_after="2026-02-01">
+    enum="SpareRendererAvailabilityWhenKilled" expires_after="2026-04-05">
   <owner>gjc@google.com</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/startup/histograms.xml b/tools/metrics/histograms/metadata/startup/histograms.xml
index 50783dbd..fbee6f5 100644
--- a/tools/metrics/histograms/metadata/startup/histograms.xml
+++ b/tools/metrics/histograms/metadata/startup/histograms.xml
@@ -114,7 +114,7 @@
 
 <histogram
     name="Startup.Android.Cold.TimeToFirstContentfulPaint.{StartActivityType}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>pasko@chromium.org</owner>
   <owner>alexilin@chromium.org</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -129,7 +129,7 @@
 
 <histogram
     name="Startup.Android.Cold.TimeToFirstContentfulPaint3.{StartActivityType}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>pasko@chromium.org</owner>
   <owner>nafisabedin@google.com</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -170,7 +170,7 @@
 
 <histogram
     name="Startup.Android.Cold.TimeToFirstNavigationCommit.{StartActivityType}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>pasko@chromium.org</owner>
   <owner>alexilin@chromium.org</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -757,7 +757,7 @@
 </histogram>
 
 <histogram name="Startup.ColdStartFromMain" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>justincohen@chromium.org</owner>
   <owner>olivierrobin@chromium.org</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -954,7 +954,7 @@
 </histogram>
 
 <histogram name="Startup.GPU.LoadTime.ApplicationStartToGpuInitialized"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>spvm@chromium.org</owner>
   <owner>etienneb@chromium.org</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -978,7 +978,7 @@
 </histogram>
 
 <histogram name="Startup.GPU.LoadTime.ChromeMainToGpuInitialized" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>spvm@chromium.org</owner>
   <owner>etienneb@chromium.org</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -1179,7 +1179,7 @@
 </histogram>
 
 <histogram name="Startup.StartupBrowserCreator.LaunchBrowser" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hidehiko@chromium.org</owner>
   <owner>junis@google.com</owner>
   <owner>cros-sw-perf+metrics@google.com</owner>
@@ -1220,7 +1220,7 @@
 </histogram>
 
 <histogram name="Startup.TimeFromMainToDidFinishLaunchingCall" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>olivierrobin@chromium.org</owner>
   <owner>justincohen@chromium.org</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -1232,7 +1232,7 @@
 </histogram>
 
 <histogram name="Startup.TimeFromMainToSceneConnection" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>olivierrobin@chromium.org</owner>
   <owner>justincohen@chromium.org</owner>
   <improvement direction="LOWER_IS_BETTER"/>
diff --git a/tools/metrics/histograms/metadata/storage/histograms.xml b/tools/metrics/histograms/metadata/storage/histograms.xml
index 47f1e36c..e1f86d2 100644
--- a/tools/metrics/histograms/metadata/storage/histograms.xml
+++ b/tools/metrics/histograms/metadata/storage/histograms.xml
@@ -324,7 +324,7 @@
 </histogram>
 
 <histogram name="IndexedDB.ClientKeepActiveRemotesCount" units="count"
-    expires_after="2026-03-29">
+    expires_after="2026-04-05">
   <owner>pmonette@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/structured_metrics/histograms.xml b/tools/metrics/histograms/metadata/structured_metrics/histograms.xml
index 0c2605d..e649f2c2 100644
--- a/tools/metrics/histograms/metadata/structured_metrics/histograms.xml
+++ b/tools/metrics/histograms/metadata/structured_metrics/histograms.xml
@@ -242,7 +242,7 @@
 </histogram>
 
 <histogram name="StructuredMetrics.StorageManager.DiskQuotaExceededDelta"
-    units="KB" expires_after="2026-01-25">
+    units="KB" expires_after="2026-04-05">
   <owner>andrewbregger@google.com</owner>
   <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
@@ -313,7 +313,7 @@
 </histogram>
 
 <histogram name="StructuredMetrics.StorageManager.RecordStatus"
-    enum="RecordStatus" expires_after="2026-02-01">
+    enum="RecordStatus" expires_after="2026-04-05">
   <owner>andrewbregger@google.com</owner>
   <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
@@ -324,7 +324,7 @@
 </histogram>
 
 <histogram name="StructuredMetrics.UploadSize" units="bytes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>andrewbregger@google.com</owner>
   <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/sync/enums.xml b/tools/metrics/histograms/metadata/sync/enums.xml
index 5157912..a911f8be 100644
--- a/tools/metrics/histograms/metadata/sync/enums.xml
+++ b/tools/metrics/histograms/metadata/sync/enums.xml
@@ -371,6 +371,7 @@
   <int value="97" label="CrossDeviceOmniboxIsInBottomPosition"/>
   <int value="98" label="AutofillWasNameAndEmailProfileUsed"/>
   <int value="99" label="CrossDeviceCrossPlatformPromosIOS16thActiveDay"/>
+  <int value="100" label="CrossDeviceSafetyCheckHomeModuleEnabled"/>
 <!-- LINT.ThenChange(/components/sync_preferences/common_syncable_prefs_database.cc:CommonSyncablePref)-->
 
 <!-- LINT.IfChange(ChromeSyncablePref) -->
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index a67c69e..c86bfbd 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -234,7 +234,7 @@
 </histogram>
 
 <histogram name="Sync.BackedOffDataType" enum="SyncDataTypes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>treib@chromium.org</owner>
   <owner>src/components/sync/OWNERS</owner>
   <summary>
@@ -680,7 +680,7 @@
 
 <histogram name="Sync.CrossUserSharingKeyPairState.DecryptPendingKeys"
     enum="CrossUserSharingKeyPairStateOnDecryptPendingKeys"
-    expires_after="2025-11-24">
+    expires_after="2026-04-05">
   <owner>rushans@google.com</owner>
   <owner>mmoskvitin@google.com</owner>
   <summary>
@@ -751,7 +751,7 @@
 </histogram>
 
 <histogram name="Sync.CustomOSSync" enum="SyncDataTypes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>mmoskvitin@google.com</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -1656,7 +1656,7 @@
 </histogram>
 
 <histogram name="Sync.PostedClientToServerMessage"
-    enum="SyncClientToServerMessageContents" expires_after="2026-02-01">
+    enum="SyncClientToServerMessageContents" expires_after="2026-04-05">
   <owner>mastiz@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <owner>src/components/sync/OWNERS</owner>
@@ -1667,7 +1667,7 @@
 </histogram>
 
 <histogram name="Sync.PostedClientToServerMessageError2" enum="SyncErrorType"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>mastiz@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <owner>src/components/sync/OWNERS</owner>
@@ -1679,7 +1679,7 @@
 </histogram>
 
 <histogram name="Sync.PostedClientToServerMessageLatency" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>mastiz@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <owner>src/components/sync/OWNERS</owner>
@@ -1690,7 +1690,7 @@
 </histogram>
 
 <histogram name="Sync.PostedClientToServerMessagePartialErrorDataType"
-    enum="SyncDataTypes" expires_after="2026-02-01">
+    enum="SyncDataTypes" expires_after="2026-04-05">
   <owner>treib@chromium.org</owner>
   <owner>src/components/sync/OWNERS</owner>
   <summary>
@@ -1705,7 +1705,7 @@
 </histogram>
 
 <histogram name="Sync.PostedDataTypeCommitRequest" enum="SyncDataTypes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>mastiz@chromium.org</owner>
   <owner>rushans@google.com</owner>
   <owner>src/components/sync/OWNERS</owner>
@@ -1719,7 +1719,7 @@
 </histogram>
 
 <histogram name="Sync.PostedDataTypeGetUpdatesRequest" enum="SyncDataTypes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>mastiz@chromium.org</owner>
   <owner>rushans@google.com</owner>
   <owner>src/components/sync/OWNERS</owner>
@@ -2021,7 +2021,7 @@
 </histogram>
 
 <histogram name="Sync.SyncablePrefIncomingIncrementalUpdate"
-    enum="SyncablePref" expires_after="2026-02-01">
+    enum="SyncablePref" expires_after="2026-04-05">
   <owner>ankushkush@google.com</owner>
   <owner>src/components/sync/OWNERS</owner>
   <summary>
@@ -2288,7 +2288,7 @@
 </histogram>
 
 <histogram name="Sync.ThrottledSomeDataTypes" enum="SyncDataTypes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>treib@chromium.org</owner>
   <owner>src/components/sync/OWNERS</owner>
   <summary>
@@ -2516,7 +2516,7 @@
 </histogram>
 
 <histogram name="Sync.URLFetchResponse"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2026-02-01">
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2026-04-05">
   <owner>mastiz@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml
index bd5e751..17af8b7 100644
--- a/tools/metrics/histograms/metadata/tab/histograms.xml
+++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -127,7 +127,7 @@
 </histogram>
 
 <histogram name="Discarding.DiscardCandidatesCount" units="tabs"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>fdoray@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -221,7 +221,7 @@
 </histogram>
 
 <histogram name="Discarding.DiscardTabOutcome" enum="DiscardTabOutcome"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>fdoray@chromium.org</owner>
   <owner>joenotcharles@chromium.org</owner>
   <summary>
@@ -274,7 +274,7 @@
 </histogram>
 
 <histogram name="Discarding.TabLifecycleUnit.DiscardLatency" units="ms"
-    expires_after="2025-12-14">
+    expires_after="2026-04-05">
   <owner>tluk@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -384,7 +384,7 @@
 </histogram>
 
 <histogram name="Tab.ExternalApplicationOpened{Cause}"
-    enum="ExternalLauncherOption" expires_after="2026-02-01">
+    enum="ExternalLauncherOption" expires_after="2026-04-05">
   <owner>olivierrobin@chromium.org</owner>
   <owner>bling-team@google.com</owner>
   <summary>
@@ -1111,7 +1111,7 @@
 </histogram>
 
 <histogram name="TabGroups.SavedTabGroupAge" units="minutes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>dljames@chromium.org</owner>
   <owner>top-chrome-desktop-ui@google.com</owner>
   <summary>
@@ -1122,7 +1122,7 @@
 </histogram>
 
 <histogram name="TabGroups.SavedTabGroupCount" units="groups"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>dljames@chromium.org</owner>
   <owner>top-chrome-desktop-ui@google.com</owner>
   <summary>
@@ -1641,7 +1641,7 @@
 </histogram>
 
 <histogram name="TabGroups.Sync.TabGroup.Opened.Reason"
-    enum="GroupOpeningSource" expires_after="2026-02-01">
+    enum="GroupOpeningSource" expires_after="2026-04-05">
   <owner>shaktisahu@chromium.org</owner>
   <owner>clank-tab-dev@google.com</owner>
   <summary>
@@ -1804,7 +1804,7 @@
 </histogram>
 
 <histogram name="TabGroups.Sync.TotalTabGroupCount" units="groups"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>shaktisahu@chromium.org</owner>
   <owner>clank-tab-dev@google.com</owner>
   <summary>
@@ -2004,7 +2004,7 @@
 </histogram>
 
 <histogram name="TabManager.Discarding.DiscardCount" units="Discards"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>fdoray@chromium.org</owner>
   <owner>catan-team@chromium.org</owner>
   <summary>
@@ -2099,7 +2099,7 @@
 </histogram>
 
 <histogram name="Tabs.Active.AbsolutePosition" units="index"
-    expires_after="2026-01-31">
+    expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>top-chrome-desktop-ui@google.com</owner>
   <summary>
@@ -2109,7 +2109,7 @@
 </histogram>
 
 <histogram name="Tabs.Active.RelativePosition" units="%"
-    expires_after="2026-01-31">
+    expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>top-chrome-desktop-ui@google.com</owner>
   <summary>
@@ -2130,7 +2130,7 @@
 </histogram>
 
 <histogram name="Tabs.ArchivedTabRestored.TabCount" units="count"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>wylieb@google.com</owner>
   <owner>clank-tab-dev@google.com</owner>
   <summary>
@@ -3286,7 +3286,7 @@
 </histogram>
 
 <histogram name="Tabs.TabSearch.Mojo.SwitchToTab" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>kerenzhu@chromium.org</owner>
   <owner>romanarora@chromium.org</owner>
   <owner>yuhengh@chromium.org</owner>
@@ -3453,7 +3453,7 @@
 </histogram>
 
 <histogram name="Tabs.TabSearch.TimeToClose" units="ms"
-    expires_after="2026-01-31">
+    expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>shibalik@chromium.org</owner>
   <owner>top-chrome-desktop-ui@google.com</owner>
@@ -3856,7 +3856,7 @@
 </histogram>
 
 <histogram name="Tabs.{DuplicateType}.{MetricType}.{WindowType}" units="tabs"
-    expires_after="2026-01-31">
+    expires_after="2026-04-05">
   <owner>emshack@chromium.org</owner>
   <owner>top-chrome-desktop-ui@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/toasts/histograms.xml b/tools/metrics/histograms/metadata/toasts/histograms.xml
index c952a223c..f0add31 100644
--- a/tools/metrics/histograms/metadata/toasts/histograms.xml
+++ b/tools/metrics/histograms/metadata/toasts/histograms.xml
@@ -8,7 +8,7 @@
 
 <histograms>
 
-<histogram name="Toast.FailedToShow" enum="ToastId" expires_after="2026-02-01">
+<histogram name="Toast.FailedToShow" enum="ToastId" expires_after="2026-04-05">
   <owner>agale@chromium.org</owner>
   <owner>stluong@chromium.org</owner>
   <owner>chrome-performance-ui-sea@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/translate/histograms.xml b/tools/metrics/histograms/metadata/translate/histograms.xml
index 95cc9ae..5d00321 100644
--- a/tools/metrics/histograms/metadata/translate/histograms.xml
+++ b/tools/metrics/histograms/metadata/translate/histograms.xml
@@ -447,7 +447,7 @@
 </histogram>
 
 <histogram name="Translate.PageLoad.InitialTargetLanguage.Origin"
-    enum="TranslateTargetLanguageOrigin" expires_after="2026-02-01">
+    enum="TranslateTargetLanguageOrigin" expires_after="2026-04-05">
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/ui/histograms.xml b/tools/metrics/histograms/metadata/ui/histograms.xml
index 19ff53e..4cfba73c 100644
--- a/tools/metrics/histograms/metadata/ui/histograms.xml
+++ b/tools/metrics/histograms/metadata/ui/histograms.xml
@@ -582,7 +582,7 @@
 </histogram>
 
 <histogram name="WebUI.ColorsStylesheetServingDuration" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>skau@chromium.org</owner>
   <owner>tluk@chromium.org</owner>
   <improvement direction="LOWER_IS_BETTER"/>
@@ -618,7 +618,7 @@
 </histogram>
 
 <histogram name="WebUI.ShownUrl" enum="WebUIUrlHashes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>kerenzhu@chromium.org</owner>
   <owner>dpapad@chromium.org</owner>
   <owner>chrome-webui@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/uma/histograms.xml b/tools/metrics/histograms/metadata/uma/histograms.xml
index ec2af79..2dd9585 100644
--- a/tools/metrics/histograms/metadata/uma/histograms.xml
+++ b/tools/metrics/histograms/metadata/uma/histograms.xml
@@ -1019,7 +1019,7 @@
 </histogram>
 
 <histogram name="UMA.TruncatedEvents.UserAction" units="events"
-    expires_after="2026-03-29">
+    expires_after="2026-04-05">
   <owner>rkaplow@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/user_data_importer/histograms.xml b/tools/metrics/histograms/metadata/user_data_importer/histograms.xml
index ae430a7..675eaf4 100644
--- a/tools/metrics/histograms/metadata/user_data_importer/histograms.xml
+++ b/tools/metrics/histograms/metadata/user_data_importer/histograms.xml
@@ -59,7 +59,7 @@
 </histogram>
 
 <histogram name="UserDataImporter.{ImportSource}.FlowDuration" units="ms"
-    expires_after="2026-01-28">
+    expires_after="2026-04-05">
   <owner>tmartino@chromium.org</owner>
   <owner>bling-transactions@google.com</owner>
   <summary>
@@ -108,7 +108,7 @@
 </histogram>
 
 <histogram name="UserDataImporter.{ImportSource}.TotalFileSize" units="KB"
-    expires_after="2026-01-28">
+    expires_after="2026-04-05">
   <owner>tmartino@chromium.org</owner>
   <owner>bling-transactions@google.com</owner>
   <summary>
@@ -119,7 +119,7 @@
 </histogram>
 
 <histogram name="UserDataImporter.{ImportSource}.{ImportDataType}.FileSize"
-    units="KB" expires_after="2026-01-28">
+    units="KB" expires_after="2026-04-05">
   <owner>tmartino@chromium.org</owner>
   <owner>bling-transactions@google.com</owner>
   <summary>
@@ -154,7 +154,7 @@
 
 <histogram
     name="UserDataImporter.{ImportSource}.{ImportDataType}.ImportDuration"
-    units="ms" expires_after="2026-01-28">
+    units="ms" expires_after="2026-04-05">
   <owner>tmartino@chromium.org</owner>
   <owner>bling-transactions@google.com</owner>
   <summary>
@@ -168,7 +168,7 @@
 
 <histogram
     name="UserDataImporter.{ImportSource}.{ImportDataType}.ImportedCount"
-    units="count" expires_after="2025-11-30">
+    units="count" expires_after="2026-04-05">
   <owner>fsenra@google.com</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -180,7 +180,7 @@
 </histogram>
 
 <histogram name="UserDataImporter.{ImportSource}.{ImportDataType}.Outcome"
-    enum="UserDataImportOutcome" expires_after="2026-01-28">
+    enum="UserDataImportOutcome" expires_after="2026-04-05">
   <owner>tmartino@chromium.org</owner>
   <owner>bling-transactions@google.com</owner>
   <summary>
@@ -192,7 +192,7 @@
 
 <histogram
     name="UserDataImporter.{ImportSource}.{ImportDataType}.PreparedCount"
-    units="items" expires_after="2026-01-28">
+    units="items" expires_after="2026-04-05">
   <owner>tmartino@chromium.org</owner>
   <owner>bling-transactions@google.com</owner>
   <summary>
@@ -206,7 +206,7 @@
 
 <histogram
     name="UserDataImporter.{ImportSource}.{ImportDataType}.PrepareDuration"
-    units="ms" expires_after="2026-01-28">
+    units="ms" expires_after="2026-04-05">
   <owner>tmartino@chromium.org</owner>
   <owner>bling-transactions@google.com</owner>
   <summary>
@@ -220,7 +220,7 @@
 </histogram>
 
 <histogram name="UserDataImporter.{ImportSource}.{ImportDataType}.SuccessRate"
-    units="%" expires_after="2026-01-28">
+    units="%" expires_after="2026-04-05">
   <owner>tmartino@chromium.org</owner>
   <owner>bling-transactions@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/v8/histograms.xml b/tools/metrics/histograms/metadata/v8/histograms.xml
index 4fd93af..8df9c89 100644
--- a/tools/metrics/histograms/metadata/v8/histograms.xml
+++ b/tools/metrics/histograms/metadata/v8/histograms.xml
@@ -1425,7 +1425,7 @@
 </histogram>
 
 <histogram name="V8.LiftoffBailoutReasons" enum="LiftoffBailoutReason"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ecmziegler@chromium.org</owner>
   <owner>clemensb@chromium.org</owner>
   <owner>wasm-runtime@chromium.org</owner>
@@ -1899,7 +1899,7 @@
 </histogram>
 
 <histogram name="V8.TurboFanOptimizeTotalBackground" units="microseconds"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>tebbi@chromium.org</owner>
   <owner>mslekova@chromium.org</owner>
   <summary>
@@ -1911,7 +1911,7 @@
 </histogram>
 
 <histogram name="V8.TurboFanOptimizeTotalForeground" units="microseconds"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>tebbi@chromium.org</owner>
   <owner>mslekova@chromium.org</owner>
   <summary>
@@ -1923,7 +1923,7 @@
 </histogram>
 
 <histogram name="V8.TurboFanOptimizeTotalTime" units="microseconds"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>tebbi@chromium.org</owner>
   <owner>mslekova@chromium.org</owner>
   <summary>
@@ -2090,7 +2090,7 @@
 </histogram>
 
 <histogram name="V8.WasmDeoptsExecutedCount" units="deopts"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>mliedtke@chromium.org</owner>
   <owner>dlehmann@chromium.org</owner>
   <owner>ecmziegler@chromium.org</owner>
@@ -2108,7 +2108,7 @@
 </histogram>
 
 <histogram name="V8.WasmDeoptsPerFunction" units="deopts"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>mliedtke@chromium.org</owner>
   <owner>dlehmann@chromium.org</owner>
   <owner>ecmziegler@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/variations/histograms.xml b/tools/metrics/histograms/metadata/variations/histograms.xml
index 95958dc..6a172eb 100644
--- a/tools/metrics/histograms/metadata/variations/histograms.xml
+++ b/tools/metrics/histograms/metadata/variations/histograms.xml
@@ -61,7 +61,7 @@
 </histogram>
 
 <histogram name="Variations.AppSeedFreshness" units="minutes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ntfschr@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -760,7 +760,7 @@
 </histogram>
 
 <histogram name="Variations.VariationsStateEncryptionStatus"
-    enum="VariationsStateEncryptionStatus" expires_after="2026-02-01">
+    enum="VariationsStateEncryptionStatus" expires_after="2026-04-05">
   <owner>svenzheng@google.com</owner>
   <owner>vivianz@google.com</owner>
   <summary>
@@ -769,7 +769,7 @@
 </histogram>
 
 <histogram name="Variations.WebViewDownloadJobInterval" units="minutes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ntfschr@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -780,7 +780,7 @@
 </histogram>
 
 <histogram name="Variations.WebViewDownloadJobQueueTime" units="minutes"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>ntfschr@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/visited_url_ranking/histograms.xml b/tools/metrics/histograms/metadata/visited_url_ranking/histograms.xml
index 32dc138..244dd58 100644
--- a/tools/metrics/histograms/metadata/visited_url_ranking/histograms.xml
+++ b/tools/metrics/histograms/metadata/visited_url_ranking/histograms.xml
@@ -116,7 +116,7 @@
 
 <histogram
     name="GroupSuggestionsService.TimeSpent.PerGroup.{GroupCreationSource}"
-    units="ms" expires_after="2026-01-01">
+    units="ms" expires_after="2026-04-05">
   <owner>mfiaz@google.com</owner>
   <owner>ssid@chromium.org</owner>
   <owner>salg@google.com</owner>
@@ -130,7 +130,7 @@
 </histogram>
 
 <histogram name="GroupSuggestionsService.TimeSpent.Total.{GroupCreationSource}"
-    units="ms" expires_after="2026-01-01">
+    units="ms" expires_after="2026-04-05">
   <owner>mfiaz@google.com</owner>
   <owner>ssid@chromium.org</owner>
   <owner>salg@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/web_apk/histograms.xml b/tools/metrics/histograms/metadata/web_apk/histograms.xml
index 6fe62c4e..b261d42 100644
--- a/tools/metrics/histograms/metadata/web_apk/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_apk/histograms.xml
@@ -128,7 +128,7 @@
 </histogram>
 
 <histogram name="WebApk.Install.RequestTokenDurationV2" units="ms"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hartmanng@chromium.org</owner>
   <owner>src/chrome/android/webapk/OWNERS</owner>
   <summary>
@@ -313,7 +313,7 @@
 </histogram>
 
 <histogram name="WebApk.Sync.SyncedWebApkCount" units="WebApks"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>hartmanng@chromium.org</owner>
   <owner>src/chrome/android/webapk/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/web_audio/histograms.xml b/tools/metrics/histograms/metadata/web_audio/histograms.xml
index 2e57043b..610afe9 100644
--- a/tools/metrics/histograms/metadata/web_audio/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_audio/histograms.xml
@@ -76,7 +76,7 @@
 </variants>
 
 <histogram name="WebAudio.AudioBuffer.Length" units="frames"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>mjwilson@chromium.org</owner>
   <owner>hongchan@chromium.org</owner>
   <owner>src/third_party/blink/renderer/modules/webaudio/OWNERS</owner>
diff --git a/tools/metrics/histograms/metadata/web_core/histograms.xml b/tools/metrics/histograms/metadata/web_core/histograms.xml
index 97f1a72..86230384 100644
--- a/tools/metrics/histograms/metadata/web_core/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_core/histograms.xml
@@ -484,8 +484,9 @@
 </histogram>
 
 <histogram name="WebCore.IndexedDB.Transaction.{TransactionType}.TimeActive2"
-    units="ms" expires_after="2025-11-09">
-  <owner>estade@chromium.org</owner>
+    units="ms" expires_after="2026-11-02">
+  <owner>leimy@chromium.org</owner>
+  <owner>evanstade@microsoft.com</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
     The time it takes for an IndexedDB transaction to commit, starting from when
@@ -497,8 +498,9 @@
 
 <histogram
     name="WebCore.IndexedDB.Transaction.{TransactionType}.TimeActive2.Foreground"
-    units="ms" expires_after="2026-01-25">
-  <owner>estade@chromium.org</owner>
+    units="ms" expires_after="2026-11-02">
+  <owner>leimy@chromium.org</owner>
+  <owner>evanstade@microsoft.com</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
     Only recorded for foreground tabs, i.e. priority 0 clients, as of when the
diff --git a/tools/metrics/histograms/metadata/web_rtc/histograms.xml b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
index a65dbc4..70e64b2 100644
--- a/tools/metrics/histograms/metadata/web_rtc/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
@@ -922,7 +922,7 @@
 </histogram>
 
 <histogram name="WebRTC.Call.EstimatedSendBitrateInKbps" units="kbps"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>holmer@chromium.org</owner>
   <summary>
     Average estimated send bitrate during a call, counted from first packet sent
@@ -1365,7 +1365,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.Duration.Worker" units="microseconds"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>handellm@google.com</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <summary>
@@ -1675,7 +1675,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.UsagePattern"
-    enum="WebRtcPeerConnectionUsagePattern" expires_after="2026-02-01">
+    enum="WebRtcPeerConnectionUsagePattern" expires_after="2026-04-05">
   <owner>hta@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <summary>
@@ -1872,7 +1872,7 @@
 </histogram>
 
 <histogram name="WebRTC.UserMediaRequest.Result2"
-    enum="MediaStreamRequestResult2" expires_after="2026-02-01">
+    enum="MediaStreamRequestResult2" expires_after="2026-04-05">
   <owner>toprice@chromium.org</owner>
   <owner>agpalak@chromium.org</owner>
   <summary>
@@ -3936,41 +3936,47 @@
 </histogram>
 
 <histogram name="WebRtcEventLogging.Api" enum="WebRtcEventLoggingApiEnum"
-    expires_after="2025-12-31">
+    expires_after="2026-11-06">
   <owner>eladalon@chromium.org</owner>
   <owner>philipel@chromium.org</owner>
+  <owner>guidou@chromium.org</owner>
   <summary>
     The result of calls to the API for the collection and uploading of WebRTC
-    event logs. Warning: this histogram was expired from 2023-11-07 to
-    2025-05-05; data may be missing.
+    event logs. Warning: this histogram was expired from from 2023-11-07 to
+    2025-05-05 and from 2025-12-31 to 2026-11-06; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="WebRtcEventLogging.NetError" units="units"
-    expires_after="2023-11-07">
+    expires_after="2026-11-06">
   <owner>eladalon@chromium.org</owner>
   <owner>manj@google.com</owner>
   <owner>dmitriyg@google.com</owner>
+  <owner>guidou@chromium.org</owner>
   <summary>
     NetError returned by the SimpleURLLoader object in charge of uploading a
-    WebRTC event log file.
+    WebRTC event log file. Warning: this histogram was expired from 2023-11-07
+    to 2026-11-06; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="WebRtcEventLogging.Upload" enum="WebRtcEventLoggingUploadEnum"
-    expires_after="2024-01-07">
+    expires_after="2026-11-06">
   <owner>eladalon@chromium.org</owner>
   <owner>manj@google.com</owner>
   <owner>dmitriyg@google.com</owner>
+  <owner>guidou@chromium.org</owner>
   <summary>
     Tracks the uploading or discarding of WebRTC event logs that were previously
-    collected.
+    collected. Warning: this histogram was expired from 2024-01-07 to
+    2025-11-06; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="WebRtcTextLogging.ApplyForStartLoggingSuccess" enum="Boolean"
-    expires_after="2026-03-10">
+    expires_after="2026-11-06">
   <owner>toprice@chromium.org</owner>
+  <owner>guidou@chromium.org</owner>
   <owner>video-cmi-apis@google.com</owner>
   <summary>
     Success or failure of a call to ApplyForStartLogging(). Failures indicate
@@ -3979,8 +3985,9 @@
 </histogram>
 
 <histogram name="WebRtcTextLogging.ConcurrentLogCount" units="counts"
-    expires_after="2026-03-10">
+    expires_after="2026-11-06">
   <owner>toprice@chromium.org</owner>
+  <owner>guidou@chromium.org</owner>
   <owner>video-cmi-apis@google.com</owner>
   <summary>
     Count of the number of concurrent webrtc loggers running, recorded at the
@@ -3989,8 +3996,9 @@
 </histogram>
 
 <histogram name="WebRtcTextLogging.StartCalled" enum="Boolean"
-    expires_after="2025-11-07">
+    expires_after="2026-11-06">
   <owner>toprice@chromium.org</owner>
+  <owner>guidou@chromium.org</owner>
   <owner>video-cmi-apis@google.com</owner>
   <summary>
     Calls to WebRtcTextLogHandler::StartLogging, as a denominator for
@@ -4000,8 +4008,9 @@
 </histogram>
 
 <histogram name="WebRtcTextLogging.Started" enum="WebRtcLoggingWebAppIdHash"
-    expires_after="2026-03-10">
+    expires_after="2026-11-06">
   <owner>toprice@chromium.org</owner>
+  <owner>guidou@chromium.org</owner>
   <owner>video-cmi-apis@google.com</owner>
   <summary>
     Rate of webrtc text log start attemps which successfully reach the STARTED
@@ -4010,8 +4019,9 @@
 </histogram>
 
 <histogram name="WebRtcTextLogging.StartError"
-    enum="WebRtcTextLoggingStartError" expires_after="2026-03-10">
+    enum="WebRtcTextLoggingStartError" expires_after="2026-11-06">
   <owner>toprice@chromium.org</owner>
+  <owner>guidou@chromium.org</owner>
   <owner>video-cmi-apis@google.com</owner>
   <summary>
     Breakdown of errors encountered starting webrtc text logging which prevent
@@ -4021,37 +4031,42 @@
 </histogram>
 
 <histogram name="WebRtcTextLogging.UploadFailureNetErrorCode"
-    enum="NetErrorCodes" expires_after="2024-10-23">
+    enum="NetErrorCodes" expires_after="2026-11-06">
   <owner>toprice@chromium.org</owner>
   <owner>olka@chromium.org</owner>
+  <owner>guidou@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <summary>
     Network error codes for WebRTC text log upload failures. Recorded when an
-    upload attempt fails. Note: The histogram was disabled between May 2022 and
-    M110, so has no data for that period.
+    upload attempt fails. Warning: this histogram was expired from 2024-10-23 to
+    2025-11-06; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="WebRtcTextLogging.UploadFailureReason"
-    enum="WebRtcLoggingUploadFailureReason" expires_after="2024-09-01">
+    enum="WebRtcLoggingUploadFailureReason" expires_after="2026-11-06">
   <owner>toprice@chromium.org</owner>
   <owner>olka@chromium.org</owner>
+  <owner>guidou@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <summary>
     Counts upload failures for WebRTC text log. Error codes for network errors
     are logged in WebRtcTextLogging.UploadFailureNetErrorCode. Recorded when an
-    upload attempt fails.
+    upload attempt fails. Warning: this histogram was expired from 2024-09-01 to
+    2025-11-06; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="WebRtcTextLogging{WebRtcLoggingEvent}"
-    enum="WebRtcLoggingWebAppIdHash" expires_after="2025-01-26">
+    enum="WebRtcLoggingWebAppIdHash" expires_after="2026-11-26">
   <owner>toprice@chromium.org</owner>
   <owner>olka@chromium.org</owner>
+  <owner>guidou@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <summary>
     Counts the number of WebRTC text log events per web application. Suffixed by
-    event. {WebRtcLoggingEvent}
+    event. {WebRtcLoggingEvent}. Warning: this histogram was expired from
+    2025-01-26 to 2025-11-06; data may be missing.
   </summary>
   <token key="WebRtcLoggingEvent">
     <variant name=".Discard" summary="Discard"/>
diff --git a/tools/metrics/histograms/metadata/webapps/histograms.xml b/tools/metrics/histograms/metadata/webapps/histograms.xml
index 6fd6b063..85ee910f 100644
--- a/tools/metrics/histograms/metadata/webapps/histograms.xml
+++ b/tools/metrics/histograms/metadata/webapps/histograms.xml
@@ -168,7 +168,7 @@
 </histogram>
 
 <histogram name="Launch.BrowserTab.WebAppLaunchHandlerClientMode"
-    enum="WebAppLaunchHandlerClientMode" expires_after="2026-02-01">
+    enum="WebAppLaunchHandlerClientMode" expires_after="2026-04-05">
   <owner>alancutter@chromium.org</owner>
   <owner>pwa-team@google.com</owner>
   <summary>
@@ -1059,7 +1059,7 @@
 </histogram>
 
 <histogram name="WebApp.Install.Result" enum="BooleanSuccess"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>phillis@chromium.org</owner>
   <owner>pwa-team@google.com</owner>
   <summary>
@@ -1159,7 +1159,7 @@
 </histogram>
 
 <histogram name="WebApp.InstalledCount.ByUser" units="count"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>dmurph@chromium.org</owner>
   <owner>pwa-team@google.com</owner>
   <summary>
@@ -1247,7 +1247,7 @@
 </histogram>
 
 <histogram name="Webapp.InstallResult.{Type}" enum="WebAppInstallResultCode"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>dmurph@chromium.org</owner>
   <owner>pwa-team@google.com</owner>
   <summary>Records the result code of Web App installs. {Type}</summary>
diff --git a/tools/metrics/histograms/metadata/webauthn/histograms.xml b/tools/metrics/histograms/metadata/webauthn/histograms.xml
index ba0ff17a..705c942 100644
--- a/tools/metrics/histograms/metadata/webauthn/histograms.xml
+++ b/tools/metrics/histograms/metadata/webauthn/histograms.xml
@@ -82,7 +82,7 @@
 </histogram>
 
 <histogram name="WebAuthentication.Android.GmsCoreGetCredentialsResult"
-    enum="GmsCoreGetCredentialsResult" expires_after="2026-02-01">
+    enum="GmsCoreGetCredentialsResult" expires_after="2026-04-05">
   <owner>derinel@google.com</owner>
   <owner>chrome-webauthn@google.com</owner>
   <summary>
@@ -150,7 +150,7 @@
 </histogram>
 
 <histogram name="WebAuthentication.ChromeOS.StartupRecordCount" units="records"
-    expires_after="2025-12-31">
+    expires_after="2026-04-05">
   <owner>martinkr@google.com</owner>
   <owner>hcyang@google.com</owner>
   <owner>chrome-webauthn@google.com</owner>
@@ -171,7 +171,7 @@
 </histogram>
 
 <histogram name="WebAuthentication.CredentialFetchDuration.GmsCore.{Source}"
-    units="ms" expires_after="2026-02-01">
+    units="ms" expires_after="2026-04-05">
   <owner>derinel@google.com</owner>
   <owner>chrome-webauthn@google.com</owner>
   <summary>
@@ -527,7 +527,7 @@
 </histogram>
 
 <histogram name="WebAuthentication.PinRenewalFailureCause"
-    enum="WebAuthenticationPinRenewalFailureCause" expires_after="2026-02-01">
+    enum="WebAuthenticationPinRenewalFailureCause" expires_after="2026-04-05">
   <owner>agl@google.com</owner>
   <owner>kenrb@chromium.org</owner>
   <summary>
@@ -572,7 +572,7 @@
 </histogram>
 
 <histogram name="WebAuthentication.U2fSignOperation"
-    enum="U2fSignOperationResult" expires_after="2026-02-01">
+    enum="U2fSignOperationResult" expires_after="2026-04-05">
   <owner>nsatragno@chromium.org</owner>
   <owner>chrome-webauthn@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/windows/histograms.xml b/tools/metrics/histograms/metadata/windows/histograms.xml
index 0da4928f..ab82dd3 100644
--- a/tools/metrics/histograms/metadata/windows/histograms.xml
+++ b/tools/metrics/histograms/metadata/windows/histograms.xml
@@ -321,7 +321,7 @@
 </histogram>
 
 <histogram name="Windows.PlatformExperienceHelper.InstallerLaunchStatus.System"
-    enum="SystemInstallerLaunchStatus" expires_after="2026-02-01">
+    enum="SystemInstallerLaunchStatus" expires_after="2026-04-05">
   <owner>alsan@chromium.org</owner>
   <owner>chrome-desktop-ui-waterloo@google.com</owner>
   <summary>
@@ -365,7 +365,7 @@
 </histogram>
 
 <histogram name="Windows.ShouldPinToTaskbarResult" enum="TaskbarPinResult"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>davidbienvenu@chromium.org</owner>
   <owner>jessemckenna@google.com</owner>
   <summary>
@@ -375,7 +375,7 @@
 </histogram>
 
 <histogram name="Windows.TaskbarPinFromFRESucceeded" enum="Boolean"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>davidbienvenu@chromium.org</owner>
   <owner>jessemckenna@google.com</owner>
   <summary>
@@ -398,7 +398,7 @@
 </histogram>
 
 <histogram name="Windows.TaskbarPinResult" enum="TaskbarPinResult"
-    expires_after="2026-02-01">
+    expires_after="2026-04-05">
   <owner>davidbienvenu@chromium.org</owner>
   <owner>jessemckenna@google.com</owner>
   <summary>
diff --git a/tools/metrics/metrics_python_tests.py b/tools/metrics/metrics_python_tests.py
index d432ad1..e8e7c382 100755
--- a/tools/metrics/metrics_python_tests.py
+++ b/tools/metrics/metrics_python_tests.py
@@ -34,7 +34,7 @@
             # directory to be added to sys.path *before* ukm), and that we run
             # the tests in a single process (jobs=1, below).
             'common/codegen_shared_test.py',
-            'private_metrics/gen_builders_test.py',
+            'private_metrics/gen_private_metrics_builders_test.py',
             'private_metrics/private_metrics_model_shared_test.py',
             'private_metrics/private_metrics_validations_test.py',
             'ukm/gen_builders_test.py',
diff --git a/tools/metrics/private_metrics/dwa_builders_template.py b/tools/metrics/private_metrics/dwa_builders_template.py
index f53aa41..2308d9f 100644
--- a/tools/metrics/private_metrics/dwa_builders_template.py
+++ b/tools/metrics/private_metrics/dwa_builders_template.py
@@ -3,11 +3,11 @@
 # found in the LICENSE file.
 """Templates for generating builder classes for DWA entries."""
 
-import codegen
+import private_metrics_codegen
 
-HEADER = codegen.Template(basename="dwa_builders.h",
-                          file_template="""
-// Generated from gen_builders.py.  DO NOT EDIT!
+HEADER = private_metrics_codegen.Template(basename="dwa_builders.h",
+                                          file_template="""
+// Generated from gen_private_metrics_builders.py.  DO NOT EDIT!
 // source: dwa.xml
 
 #ifndef {file.guard_path}
@@ -28,7 +28,7 @@
 
 #endif  // {file.guard_path}
 """,
-                          event_template="""
+                                          event_template="""
 class {event.name} final : public ::dwa::internal::DwaEntryBuilderBase {{
  public:
   explicit {event.name}();
@@ -45,20 +45,20 @@
 {study_code}
 }};
 """,
-                          metric_template="""
+                                          metric_template="""
   static const char k{metric.name}Name[];
   static constexpr uint64_t k{metric.name}NameHash = UINT64_C({metric.hash});
   {event.name}& Set{metric.name}(int64_t value);
 """,
-                          study_template="""
+                                          study_template="""
   static constexpr char k{study.name}Name[] = "{study.raw_name}";
   static constexpr uint32_t k{study.name}NameHash = UINT32_C({study.hash});
 """)
 
-IMPL = codegen.Template(
+IMPL = private_metrics_codegen.Template(
     basename="dwa_builders.cc",
     file_template="""
-// Generated from gen_builders.py.  DO NOT EDIT!
+// Generated from gen_private_metrics_builders.py.  DO NOT EDIT!
 // source: dwa.xml
 
 #include "{file.dir_path}dwa_builders.h"
diff --git a/tools/metrics/private_metrics/dwa_decode_template.py b/tools/metrics/private_metrics/dwa_decode_template.py
index 73d4312..a1682af 100644
--- a/tools/metrics/private_metrics/dwa_decode_template.py
+++ b/tools/metrics/private_metrics/dwa_decode_template.py
@@ -3,11 +3,11 @@
 # found in the LICENSE file.
 """A template for generating hash decoding code."""
 
-import codegen
+import private_metrics_codegen
 
-HEADER = codegen.Template(basename="dwa_decode.h",
-                          file_template="""
-// Generated from gen_builders.py.  DO NOT EDIT!
+HEADER = private_metrics_codegen.Template(basename="dwa_decode.h",
+                                          file_template="""
+// Generated from gen_private_metrics_builders.py.  DO NOT EDIT!
 // source: dwa.xml
 
 #ifndef {file.guard_path}
@@ -34,13 +34,13 @@
 
 #endif  // {file.guard_path}
 """,
-                          event_template="",
-                          metric_template="",
-                          study_template="")
+                                          event_template="",
+                                          metric_template="",
+                                          study_template="")
 
-IMPL = codegen.Template(basename="dwa_decode.cc",
-                        file_template="""
-// Generated from gen_builders.py.  DO NOT EDIT!
+IMPL = private_metrics_codegen.Template(basename="dwa_decode.cc",
+                                        file_template="""
+// Generated from gen_private_metrics_builders.py.  DO NOT EDIT!
 // source: dwa.xml
 
 #include "{file.dir_path}/dwa_decode.h"
@@ -58,7 +58,7 @@
 }}  // namespace builders
 }}  // namespace dwa
 """,
-                        event_template="""
+                                        event_template="""
     {{
       UINT64_C({event.hash}),
       {{
@@ -72,10 +72,10 @@
       }}
     }},
 """,
-                        metric_template="""
+                                        metric_template="""
     {{{event.name}::k{metric.name}NameHash, {event.name}::k{metric.name}Name}},
 """,
-                        study_template="""
+                                        study_template="""
     {{{event.name}::k{study.name}NameHash, {event.name}::k{study.name}Name}},
 """)
 
diff --git a/tools/metrics/private_metrics/gen_builders.gni b/tools/metrics/private_metrics/gen_private_metrics_builders.gni
similarity index 91%
rename from tools/metrics/private_metrics/gen_builders.gni
rename to tools/metrics/private_metrics/gen_private_metrics_builders.gni
index 080ab7d..2d7daf8b 100644
--- a/tools/metrics/private_metrics/gen_builders.gni
+++ b/tools/metrics/private_metrics/gen_private_metrics_builders.gni
@@ -12,13 +12,13 @@
 
   # Generate builders for specific Private Metrics type.
   action("generate_${target_name}") {
-    script = "//tools/metrics/private_metrics/gen_builders.py"
+    script = "//tools/metrics/private_metrics/gen_private_metrics_builders.py"
 
     # Re-generate the outputs if the codegen code changes:
     inputs = [
       "//tools/metrics/private_metrics/dwa_builders_template.py",
       "//tools/metrics/private_metrics/dwa_decode_template.py",
-      "//tools/metrics/private_metrics/codegen.py",
+      "//tools/metrics/private_metrics/private_metrics_codegen.py",
     ]
 
     sources = [ "//tools/metrics/private_metrics/${invoker.type}.xml" ]
diff --git a/tools/metrics/private_metrics/gen_builders.py b/tools/metrics/private_metrics/gen_private_metrics_builders.py
similarity index 100%
rename from tools/metrics/private_metrics/gen_builders.py
rename to tools/metrics/private_metrics/gen_private_metrics_builders.py
diff --git a/tools/metrics/private_metrics/gen_builders_test.py b/tools/metrics/private_metrics/gen_private_metrics_builders_test.py
similarity index 93%
rename from tools/metrics/private_metrics/gen_builders_test.py
rename to tools/metrics/private_metrics/gen_private_metrics_builders_test.py
index ff24da3..0544ef4 100755
--- a/tools/metrics/private_metrics/gen_builders_test.py
+++ b/tools/metrics/private_metrics/gen_private_metrics_builders_test.py
@@ -3,17 +3,20 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import os
 import unittest
 
-import codegen
+import private_metrics_codegen
 import dwa_builders_template
 import dwa_decode_template
 import dwa_model
-import os
+import private_metrics_model_shared
 
 _FILE_DIR = os.path.dirname(__file__)
 
 
+# TODO(crbug.com/448819123): Fix this test suite
+@unittest.skip("decayed test suite")
 class GenBuildersDwaTest(unittest.TestCase):
 
   def setUp(self) -> None:
@@ -22,19 +25,19 @@
       self.data = dwa_model.DWA_XML_TYPE.Parse(f.read())
     with open(_FILE_DIR + '/dwa_test.xml') as f:
       self.dwa_test_data = dwa_model.DWA_XML_TYPE.Parse(f.read())
-    event = self.data[dwa_model._EVENT_TYPE.tag][0]
-    metric = event[dwa_model._METRIC_TYPE.tag][0]
-    study = event[dwa_model._STUDY_TYPE.tag][0]
+    event = self.data[private_metrics_model_shared.EVENT_TYPE.tag][0]
+    metric = event[private_metrics_model_shared.METRIC_TYPE.tag][0]
+    study = event[private_metrics_model_shared.STUDY_TYPE.tag][0]
     self.assertIsNotNone(event)
     self.assertIsNotNone(metric)
     self.assertIsNotNone(study)
-    self.event_info = codegen.EventInfo(event)
-    self.metric_info = codegen.MetricInfo(metric)
-    self.study_info = codegen.StudyInfo(study)
+    self.event_info = private_metrics_codegen.EventInfo(event)
+    self.metric_info = private_metrics_codegen.MetricInfo(metric)
+    self.study_info = private_metrics_codegen.StudyInfo(study)
 
   def testBuildersHeaderOutput(self) -> None:
-    # Not using codegen.Template.WriteFile to avoid non-deterministic test
-    # behaviour after writing to disk.
+    # Not using private_metrics_codegen.Template.WriteFile to avoid
+    # non-deterministic test behavior after writing to disk.
     builders_header_output = dwa_builders_template.HEADER._StampFileCode(
         self.relpath, self.data)
     self.assertIsNotNone(builders_header_output)
diff --git a/tools/metrics/private_metrics/codegen.py b/tools/metrics/private_metrics/private_metrics_codegen.py
similarity index 99%
rename from tools/metrics/private_metrics/codegen.py
rename to tools/metrics/private_metrics/private_metrics_codegen.py
index f5ac195..65ec94b 100644
--- a/tools/metrics/private_metrics/codegen.py
+++ b/tools/metrics/private_metrics/private_metrics_codegen.py
@@ -5,7 +5,6 @@
 
 import os
 import sys
-import dwa_model
 import private_metrics_model_shared
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common'))
diff --git a/tools/perf/chromium.perf.processors.json b/tools/perf/chromium.perf.processors.json
index 8824693..9bdcab7 100644
--- a/tools/perf/chromium.perf.processors.json
+++ b/tools/perf/chromium.perf.processors.json
@@ -18,6 +18,15 @@
       "script": "//tools/perf/process_perf_results.py"
     }
   },
+  "mac-m4-mini-processor-perf": {
+    "merge": {
+      "args": [
+        "--lightweight",
+        "--upload-skia-json"
+      ],
+      "script": "//tools/perf/process_perf_results.py"
+    }
+  },
   "win-10-processor-perf": {
     "merge": {
       "args": [
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 3a8eab9..ca487cd 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -143,7 +143,7 @@
     'mac-m1_mini_2020-perf-pgo',
     'mac-m2-pro-perf',
     'mac-m3-pro-perf',
-    'mac-m4-mini-perf',
+    'mac-m4-mini-processor-perf',
     'win-10-processor-perf',
     'win-10_amd_laptop-perf',
     'win-10_laptop_low_end-processor-perf',
@@ -1136,6 +1136,10 @@
                 'Mac16,10_arm64-64-Apple_M4_apple m4_32768_APPLE SSD AP2048Z',
         },
     },
+    'mac-m4-mini-processor-perf': {
+        'platform': 'linux',
+        'perf_processor': True,
+    },
     'win-10_amd_laptop-perf': {
         'tests': [
             {
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index a84fc7c..420491e 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v52.0/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "b88ee6c5332f8144c4887b1a7ad41ace54b63c38",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/b6e6c4d600d07e3974db4db36e0010291ce49441/trace_processor_shell.exe"
+            "hash": "31e592d813308fb348ef12fc0d7f7b06ee94d7ac",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/d57eda6f192a4d5f147531d3f63355b5e36c5c78/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "46d798c1864490cbb2ee053d6eda436184470e69",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/ebf44e57a3b734c5281bdff53d9945805486004e/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "d00329b1c447e0ad85417f760879fc92dc8d0cbf",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/b6e6c4d600d07e3974db4db36e0010291ce49441/trace_processor_shell"
+            "hash": "56058bc6149b63268393a60fc0fff4e5448b390a",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/d57eda6f192a4d5f147531d3f63355b5e36c5c78/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 74b1be6..5a5efdd5 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -544,6 +544,16 @@
 crbug.com/1452148 [ android-go ] v8.browsing_mobile/browse:media:tiktok_infinite_scroll:2021 [ Skip ]
 crbug.com/435172284 [ android-pixel-4 ] v8.browsing_mobile/browse:social:facebook_infinite_scroll:2018 [ Skip ]
 crbug.com/435172284 [ android-pixel-4 ] v8.browsing_mobile/browse:news:nytimes:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-fold ] v8.browsing_mobile/browse:media:imgur:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-fold ] v8.browsing_mobile/browse:news:nytimes:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-fold ] v8.browsing_mobile/browse:social:instagram:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-fold ] v8.browsing_mobile/browse:social:twitter:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-fold ] v8.browsing_mobile/browse:tools:maps:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-tablet ] v8.browsing_mobile/browse:media:imgur:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-tablet ] v8.browsing_mobile/browse:news:nytimes:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-tablet ] v8.browsing_mobile/browse:social:instagram:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-tablet ] v8.browsing_mobile/browse:social:twitter:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-tablet ] v8.browsing_mobile/browse:tools:maps:2019 [ Skip ]
 # Fuchsia does not support new tab nor omnibox
 [ fuchsia-board-nelson ] v8.browsing_mobile/browse:chrome:newtab:2019 [ Skip ]
 [ fuchsia-board-nelson ] v8.browsing_mobile/browse:chrome:omnibox:2019 [ Skip ]
@@ -603,38 +613,48 @@
 crbug.com/435172284 [ android-pixel-4 ] v8.browsing_mobile-future/browse:news:nytimes:2019 [ Skip ]
 crbug.com/436580154 [ android-pixel-6 ] v8.browsing_mobile-future/browse:news:nytimes:2019 [ Skip ]
 crbug.com/436580154 [ android-pixel-6-pro ] v8.browsing_mobile-future/browse:news:nytimes:2019 [ Skip ]
-crbug.com/436580154 [ android-pixel-6 ] v8.browsing_mobile/browse:news:nytimes:2019 [ Skip ]
-crbug.com/436580154 [ android-pixel-6-pro ] v8.browsing_mobile/browse:news:nytimes:2019 [ Skip ]
+crbug.com/436580154 [ android-pixel-6 ] v8.browsing_mobile-future/browse:news:nytimes:2019 [ Skip ]
+crbug.com/436580154 [ android-pixel-6-pro ] v8.browsing_mobile-future/browse:news:nytimes:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-fold ] v8.browsing_mobile-future/browse:media:imgur:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-fold ] v8.browsing_mobile-future/browse:news:nytimes:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-fold ] v8.browsing_mobile-future/browse:social:instagram:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-fold ] v8.browsing_mobile-future/browse:social:twitter:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-fold ] v8.browsing_mobile-future/browse:tools:maps:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-tablet ] v8.browsing_mobile-future/browse:media:imgur:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-tablet ] v8.browsing_mobile-future/browse:news:nytimes:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-tablet ] v8.browsing_mobile-future/browse:social:instagram:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-tablet ] v8.browsing_mobile-future/browse:social:twitter:2019 [ Skip ]
+crbug.com/448631403 [ android-pixel-tablet ] v8.browsing_mobile-future/browse:tools:maps:2019 [ Skip ]
 # Fuchsia does not support new tab nor omnibox
-[ fuchsia-board-nelson ] v8.browsing_mobile/browse:chrome:newtab:2019 [ Skip ]
-[ fuchsia-board-nelson ] v8.browsing_mobile/browse:chrome:omnibox:2019 [ Skip ]
-[ fuchsia-board-sherlock ] v8.browsing_mobile/browse:chrome:newtab:2019 [ Skip ]
-[ fuchsia-board-sherlock ] v8.browsing_mobile/browse:chrome:omnibox:2019 [ Skip ]
+[ fuchsia-board-nelson ] v8.browsing_mobile-future/browse:chrome:newtab:2019 [ Skip ]
+[ fuchsia-board-nelson ] v8.browsing_mobile-future/browse:chrome:omnibox:2019 [ Skip ]
+[ fuchsia-board-sherlock ] v8.browsing_mobile-future/browse:chrome:newtab:2019 [ Skip ]
+[ fuchsia-board-sherlock ] v8.browsing_mobile-future/browse:chrome:omnibox:2019 [ Skip ]
 # The default scrolling height is too much for fuchsia devices.
-[ fuchsia-board-nelson ] v8.browsing_mobile/browse:media:flickr_infinite_scroll:2019 [ Skip ]
-[ fuchsia-board-sherlock ] v8.browsing_mobile/browse:media:flickr_infinite_scroll:2019 [ Skip ]
-[ fuchsia-board-nelson ] v8.browsing_mobile/browse:tech:discourse_infinite_scroll:2018 [ Skip ]
-[ fuchsia-board-sherlock ] v8.browsing_mobile/browse:tech:discourse_infinite_scroll:2018 [ Skip ]
+[ fuchsia-board-nelson ] v8.browsing_mobile-future/browse:media:flickr_infinite_scroll:2019 [ Skip ]
+[ fuchsia-board-sherlock ] v8.browsing_mobile-future/browse:media:flickr_infinite_scroll:2019 [ Skip ]
+[ fuchsia-board-nelson ] v8.browsing_mobile-future/browse:tech:discourse_infinite_scroll:2018 [ Skip ]
+[ fuchsia-board-sherlock ] v8.browsing_mobile-future/browse:tech:discourse_infinite_scroll:2018 [ Skip ]
 # Unexpected loading forever, need extra investigation.
-[ fuchsia-board-nelson ] v8.browsing_mobile/browse:social:twitter:2019 [ Skip ]
-[ fuchsia-board-nelson ] v8.browsing_mobile/browse:news:globo:2019 [ Skip ]
-[ fuchsia-board-nelson ] v8.browsing_mobile/browse:news:businessinsider:2021 [ Skip ]
-[ fuchsia-board-nelson ] v8.browsing_mobile/browse:tools:maps:2019 [ Skip ]
-[ fuchsia-board-nelson ] v8.browsing_mobile/browse:social:pinterest_infinite_scroll:2021 [ Skip ]
-[ fuchsia-board-nelson ] v8.browsing_mobile/browse:media:imgur:2019 [ Skip ]
-[ fuchsia-board-nelson ] v8.browsing_mobile/browse:news:nytimes:2019 [ Skip ]
-[ fuchsia-board-nelson ] v8.browsing_mobile/browse:social:instagram:2019 [ Skip ]
-[ fuchsia-board-sherlock ] v8.browsing_mobile/browse:social:twitter:2019 [ Skip ]
-[ fuchsia-board-sherlock ] v8.browsing_mobile/browse:news:globo:2019 [ Skip ]
-[ fuchsia-board-sherlock ] v8.browsing_mobile/browse:news:businessinsider:2021 [ Skip ]
-[ fuchsia-board-sherlock ] v8.browsing_mobile/browse:tools:maps:2019 [ Skip ]
-[ fuchsia-board-sherlock ] v8.browsing_mobile/browse:social:pinterest_infinite_scroll:2021 [ Skip ]
-[ fuchsia-board-sherlock ] v8.browsing_mobile/browse:media:imgur:2019 [ Skip ]
-[ fuchsia-board-sherlock ] v8.browsing_mobile/browse:news:nytimes:2019 [ Skip ]
-[ fuchsia-board-sherlock ] v8.browsing_mobile/browse:social:instagram:2019 [ Skip ]
+[ fuchsia-board-nelson ] v8.browsing_mobile-future/browse:social:twitter:2019 [ Skip ]
+[ fuchsia-board-nelson ] v8.browsing_mobile-future/browse:news:globo:2019 [ Skip ]
+[ fuchsia-board-nelson ] v8.browsing_mobile-future/browse:news:businessinsider:2021 [ Skip ]
+[ fuchsia-board-nelson ] v8.browsing_mobile-future/browse:tools:maps:2019 [ Skip ]
+[ fuchsia-board-nelson ] v8.browsing_mobile-future/browse:social:pinterest_infinite_scroll:2021 [ Skip ]
+[ fuchsia-board-nelson ] v8.browsing_mobile-future/browse:media:imgur:2019 [ Skip ]
+[ fuchsia-board-nelson ] v8.browsing_mobile-future/browse:news:nytimes:2019 [ Skip ]
+[ fuchsia-board-nelson ] v8.browsing_mobile-future/browse:social:instagram:2019 [ Skip ]
+[ fuchsia-board-sherlock ] v8.browsing_mobile-future/browse:social:twitter:2019 [ Skip ]
+[ fuchsia-board-sherlock ] v8.browsing_mobile-future/browse:news:globo:2019 [ Skip ]
+[ fuchsia-board-sherlock ] v8.browsing_mobile-future/browse:news:businessinsider:2021 [ Skip ]
+[ fuchsia-board-sherlock ] v8.browsing_mobile-future/browse:tools:maps:2019 [ Skip ]
+[ fuchsia-board-sherlock ] v8.browsing_mobile-future/browse:social:pinterest_infinite_scroll:2021 [ Skip ]
+[ fuchsia-board-sherlock ] v8.browsing_mobile-future/browse:media:imgur:2019 [ Skip ]
+[ fuchsia-board-sherlock ] v8.browsing_mobile-future/browse:news:nytimes:2019 [ Skip ]
+[ fuchsia-board-sherlock ] v8.browsing_mobile-future/browse:social:instagram:2019 [ Skip ]
 # Known renderer crash - resource exhaustion.
-[ fuchsia-board-nelson ] v8.browsing_mobile/browse:news:cnn:2021 [ Skip ]
-[ fuchsia-board-sherlock ] v8.browsing_mobile/browse:news:cnn:2021 [ Skip ]
+[ fuchsia-board-nelson ] v8.browsing_mobile-future/browse:news:cnn:2021 [ Skip ]
+[ fuchsia-board-sherlock ] v8.browsing_mobile-future/browse:news:cnn:2021 [ Skip ]
 
 # Benchmark: v8.runtime_stats.top_25
 crbug.com/954229 [ mac ] v8.runtime_stats.top_25/* [ Skip ]
diff --git a/tools/variations/fieldtrial_util_unittest.py b/tools/variations/fieldtrial_util_unittest.py
index 54f5b4b..87d7087 100644
--- a/tools/variations/fieldtrial_util_unittest.py
+++ b/tools/variations/fieldtrial_util_unittest.py
@@ -278,7 +278,7 @@
 
     # For each flag, we expect alphabetical ordering of the pieces merged as
     # they are sorted first.
-    self.assertEquals(args, [
+    self.assertEqual(args, [
         '--foo',
         '--bar',
         '--disable-features=Feature1,Feature2,Feature3',
diff --git a/ui/actions/BUILD.gn b/ui/actions/BUILD.gn
index 4812184..f62bb41 100644
--- a/ui/actions/BUILD.gn
+++ b/ui/actions/BUILD.gn
@@ -9,7 +9,6 @@
   sources = [
     "action_id.h",
     "action_id_macros.inc",
-    "action_utils.h",
     "actions.h",
   ]
 
diff --git a/ui/actions/action_utils.h b/ui/actions/action_utils.h
deleted file mode 100644
index a207326..0000000
--- a/ui/actions/action_utils.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_ACTIONS_ACTION_UTILS_H_
-#define UI_ACTIONS_ACTION_UTILS_H_
-
-#include "ui/base/metadata/metadata_utils.h"
-
-namespace actions {
-
-class ActionItem;
-
-template <typename A>
-bool IsActionItemClass(ActionItem* action_item) {
-  return ui::metadata::IsClass<A, ActionItem>(action_item);
-}
-
-template <typename A>
-std::unique_ptr<A> ToActionItemClass(std::unique_ptr<ActionItem> action_item) {
-  CHECK(IsActionItemClass<A>(action_item.get()));
-  return std::unique_ptr<A>(static_cast<A*>(action_item.release()));
-}
-
-enum class ActionPinnableState {
-  kNotPinnable = 0,
-  kPinnable = 1,
-  kEnterpriseControlled = 2,
-};
-
-}  // namespace actions
-
-#endif  // UI_ACTIONS_ACTION_UTILS_H_
diff --git a/ui/actions/actions.cc b/ui/actions/actions.cc
index bb0eb837..08beec08 100644
--- a/ui/actions/actions.cc
+++ b/ui/actions/actions.cc
@@ -11,7 +11,6 @@
 #include <string_view>
 
 #include "base/no_destructor.h"
-#include "ui/actions/action_utils.h"
 #include "ui/base/class_property.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 
diff --git a/ui/actions/actions.h b/ui/actions/actions.h
index 019c7fa..7726857 100644
--- a/ui/actions/actions.h
+++ b/ui/actions/actions.h
@@ -20,7 +20,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "ui/actions/action_id.h"
-#include "ui/actions/action_utils.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/class_property.h"
 #include "ui/base/metadata/metadata_header_macros.h"
@@ -508,6 +507,11 @@
   ui::ImageModel stateful_image_;
 };
 
+template <typename A>
+bool IsActionItemClass(ActionItem* action_item) {
+  return ui::metadata::IsClass<A, ActionItem>(action_item);
+}
+
 class COMPONENT_EXPORT(ACTIONS) ActionManager
     : public ui::metadata::MetaDataProvider {
  public:
@@ -612,6 +616,12 @@
   static StringToActionIdMap& GetStringToActionIdMap();
 };
 
+enum class ActionPinnableState {
+  kNotPinnable = 0,
+  kPinnable = 1,
+  kEnterpriseControlled = 2,
+};
+
 COMPONENT_EXPORT(ACTIONS)
 extern const ui::ClassProperty<
     std::underlying_type_t<ActionPinnableState>>* const kActionItemPinnableKey;
diff --git a/ui/android/display_android_manager.cc b/ui/android/display_android_manager.cc
index 5d944fb..4220fd0d 100644
--- a/ui/android/display_android_manager.cc
+++ b/ui/android/display_android_manager.cc
@@ -159,18 +159,18 @@
         gfx::ColorSpace::CreateSRGB(), viz::SinglePlaneFormat::kRGBA_8888);
     display_color_spaces.SetHDRMaxLuminanceRelative(hdr_max_luminance_ratio);
     for (auto needs_alpha : {true, false}) {
-      // TODO: Low-end devices should specify RGB_565 as the buffer format for
-      // opaque content.
-      display_color_spaces.SetOutputColorSpaceAndBufferFormat(
+      // TODO: Low-end devices should specify RGB_565 as the format for opaque
+      // content.
+      display_color_spaces.SetOutputColorSpaceAndFormat(
           gfx::ContentColorUsage::kSRGB, needs_alpha, cs_for_srgb,
-          gfx::BufferFormat::RGBA_8888);
-      display_color_spaces.SetOutputColorSpaceAndBufferFormat(
+          viz::SinglePlaneFormat::kRGBA_8888);
+      display_color_spaces.SetOutputColorSpaceAndFormat(
           gfx::ContentColorUsage::kWideColorGamut, needs_alpha, cs_for_wcg,
-          gfx::BufferFormat::RGBA_8888);
+          viz::SinglePlaneFormat::kRGBA_8888);
       // TODO(crbug.com/40263227): Use 10-bit surfaces for opaque HDR.
-      display_color_spaces.SetOutputColorSpaceAndBufferFormat(
+      display_color_spaces.SetOutputColorSpaceAndFormat(
           gfx::ContentColorUsage::kHDR, needs_alpha, cs_for_hdr,
-          gfx::BufferFormat::RGBA_8888);
+          viz::SinglePlaneFormat::kRGBA_8888);
     }
     display->SetColorSpaces(display_color_spaces);
   }
diff --git a/ui/android/java/res/values/values.xml b/ui/android/java/res/values/values.xml
index fddfe8dd..a2ff447 100644
--- a/ui/android/java/res/values/values.xml
+++ b/ui/android/java/res/values/values.xml
@@ -22,5 +22,6 @@
     <item type="fraction" format="fraction" name="menu_start_animation_pivot_x">5%</item>
 
     <!-- Flyout Menu Constants-->
-    <item type="integer" format="integer" name="flyout_menu_delay_in_ms">100</item>
+    <item type="integer" format="integer" name="flyout_menu_delay_in_ms">300</item>
+    <item type="integer" format="integer" name="flyout_menu_hover_exit_delay_in_ms">50</item>
 </resources>
diff --git a/ui/android/java/src/org/chromium/ui/display/DisplayUtil.java b/ui/android/java/src/org/chromium/ui/display/DisplayUtil.java
index 49bbc17d..ab982e83 100644
--- a/ui/android/java/src/org/chromium/ui/display/DisplayUtil.java
+++ b/ui/android/java/src/org/chromium/ui/display/DisplayUtil.java
@@ -519,20 +519,45 @@
     }
 
     /**
-     * If the provided Rect fits fully inside given display's bounds, this method returns a copy of
-     * the provided Rect.
+     * Adjusts {@code inputRect} to fit inside {@code limitingRect}.
      *
-     * <p>Otherwise, the Rect returned will be a copy of the provided Rect modified so that it is
-     * fully inside given display's bounds and is the closest match to the provided Rect,
+     * <p>If {@code inputRect} fits fully inside {@code limitingRect}, this method returns a copy of
+     * {@code inputRect}.
+     *
+     * <p>Otherwise, the returned {@link Rect} will be a copy of {@code inputRect} modified so that
+     * it is fully inside {@code limitingRect} and is the closest match to {@code inputRect},
      * prioritising preserving original width and height first, then minimizing the Manhattan
-     * distance between the original Rect and the adjusted one.
+     * distance between {@code inputRect} and the adjusted one.
      *
-     * <p>If the provided Rect is longer than given display's bounds in precisely one axis, the
-     * displacement alongside the other axis will be minimised between the provided Rect and the
+     * <p>If {@code inputRect} is longer than {@code limitingRect} in precisely one axis, the
+     * displacement alongside the other axis will be minimised between {@code inputRect} and the
      * adjusted one.
      *
-     * <p>If the provided Rect is longer than given display's bounds in both axes, the display's
-     * bounds will be returned.
+     * <p>If {@code inputRect} is longer than {@code limitingRect} in both axes, {@code
+     * limitingRect} will be returned.
+     *
+     * @param inputRect The {@link Rect} to adjust.
+     * @param limitingRect The {@link Rect} that defines the bounds.
+     * @return A new {@link Rect}, guaranteed to be fully within {@code limitingRect}.
+     */
+    @SuppressWarnings("CheckResult")
+    public static Rect clampRect(Rect inputRect, Rect limitingRect) {
+        Rect output = new Rect(inputRect);
+
+        output.offset(Math.max(limitingRect.left - output.left, 0), 0);
+        output.offset(Math.min(limitingRect.right - output.right, 0), 0);
+        output.offset(0, Math.max(limitingRect.top - output.top, 0));
+        output.offset(0, Math.min(limitingRect.bottom - output.bottom, 0));
+
+        output.intersect(limitingRect);
+
+        return output;
+    }
+
+    /**
+     * Adjusts the given bounds to fit the given display.
+     *
+     * <p>Please see {@link #clampRect(Rect, Rect)} for how the bounds are adjusted.
      *
      * @param boundsPx The rectangle to adjust, in pixels. Its coordinates should be relative to the
      *     display, with (0, 0) at the top-left corner and positive axes going rightward and
@@ -541,19 +566,8 @@
      * @return A new Rect, guaranteed to be fully within the display bounds. Uses the same
      *     coordinate system as the initial Rect.
      */
-    @SuppressWarnings("CheckResult")
     public static Rect clampWindowToDisplay(Rect boundsPx, DisplayAndroid display) {
-        final Rect output = new Rect(boundsPx);
-        final Rect limitingBounds = display.getLocalBounds();
-
-        output.offset(Math.max(limitingBounds.left - output.left, 0), 0);
-        output.offset(Math.min(limitingBounds.right - output.right, 0), 0);
-        output.offset(0, Math.max(limitingBounds.top - output.top, 0));
-        output.offset(0, Math.min(limitingBounds.bottom - output.bottom, 0));
-
-        output.intersect(limitingBounds);
-
-        return output;
+        return clampRect(boundsPx, display.getLocalBounds());
     }
 
     public static void setCarmaPhase1Version2ComplianceForTesting(
diff --git a/ui/android/java/src/org/chromium/ui/listmenu/ListMenuFlyoutController.java b/ui/android/java/src/org/chromium/ui/listmenu/ListMenuFlyoutController.java
index f238813..4a44791 100644
--- a/ui/android/java/src/org/chromium/ui/listmenu/ListMenuFlyoutController.java
+++ b/ui/android/java/src/org/chromium/ui/listmenu/ListMenuFlyoutController.java
@@ -35,6 +35,9 @@
     private @Nullable View mPendingFlyoutParentView;
     private List<ListItem> mLastHighlightedPath = new ArrayList<ListItem>();
 
+    private @Nullable Handler mHoverExitDelayHandler;
+    private @Nullable Runnable mPendingHoverExitRunnable;
+
     /**
      * A data class holding a flyout popup window and the (optional) parent ListItem that triggered
      * it. The root popup will have a null parentItem.
@@ -119,16 +122,42 @@
             int levelOfHoveredItem,
             @Nullable Boolean drillDownOverrideValue,
             List<ListItem> highlightPath) {
+        if (mPendingHoverExitRunnable != null) {
+            assert mHoverExitDelayHandler != null;
+            mHoverExitDelayHandler.removeCallbacks(mPendingHoverExitRunnable);
+            mPendingHoverExitRunnable = null;
+            mHoverExitDelayHandler = null;
+        }
+
         switch (event.getAction()) {
             case MotionEvent.ACTION_HOVER_ENTER:
                 onItemHovered(
                         item, view, levelOfHoveredItem, drillDownOverrideValue, highlightPath);
                 return true;
             case MotionEvent.ACTION_HOVER_EXIT:
-                if (item.model.get(IS_HIGHLIGHTED)) {
-                    updateHighlightPath(highlightPath.subList(0, highlightPath.size() - 1));
-                }
-                cancelFlyoutDelay(view);
+                // Update highlights after a short delay. This is to prevent UI flicker when the
+                // user moves the pointer from the parent item view to a flyout item view. We
+                // receive an {@code ACTION_HOVER_EXIT} event to the parent view right before we
+                // receive an {@code ACTION_HOVER_ENTER} event on the flyout view. If we faithfully
+                // follow these, the parent item momentarily loses the hover style, so we ignore the
+                // first exit event in case it's immediately followed by an enter event.
+                mPendingHoverExitRunnable =
+                        () -> {
+                            if (item.model.get(IS_HIGHLIGHTED)) {
+                                updateHighlightPath(
+                                        highlightPath.subList(0, highlightPath.size() - 1));
+                            }
+                            cancelFlyoutDelay(view);
+                            mPendingHoverExitRunnable = null;
+                        };
+                mHoverExitDelayHandler = view.getHandler();
+                assert mHoverExitDelayHandler != null;
+                mHoverExitDelayHandler.postDelayed(
+                        mPendingHoverExitRunnable,
+                        view.getContext()
+                                .getResources()
+                                .getInteger(R.integer.flyout_menu_hover_exit_delay_in_ms));
+
                 // We only want to remove the flyout popups when the user hovers
                 // over another item. We don't close the flyout popup even when the
                 // item itself loses hover.
diff --git a/ui/android/junit/src/org/chromium/ui/display/DisplayUtilTest.java b/ui/android/junit/src/org/chromium/ui/display/DisplayUtilTest.java
index 414ea43c..a8a80802 100644
--- a/ui/android/junit/src/org/chromium/ui/display/DisplayUtilTest.java
+++ b/ui/android/junit/src/org/chromium/ui/display/DisplayUtilTest.java
@@ -435,7 +435,86 @@
     }
 
     @Test
-    public void testClampWindowToDisplay_boundsInsideDisplay() {
+    public void testClampRect_inputRectInsideLimitingRect() {
+        Rect limitingRect = new Rect(0, 0, 1920, 1080);
+        Rect inputRect = new Rect(100, 200, 700, 800);
+        assertEquals(
+                "The inputRect should have been preserved as it's' already inside limitingRect",
+                inputRect,
+                DisplayUtil.clampRect(inputRect, limitingRect));
+    }
+
+    @Test
+    public void testClampRect_inputRectPartiallyOutsideLimitingRectInHorizontalAxis() {
+        Rect limitingRect = new Rect(0, 0, 1000, 1000);
+        Rect inputRect = new Rect(-100, 200, 700, 800);
+
+        // Size can be preserved, there is exactly one Rect with given size distant by 100 px in
+        // Manhattan metric that fits inside the limitingRect, and there is no valid Rect with given
+        // size closer than 100 px.
+        Rect expectedRect = new Rect(0, 200, 800, 800);
+        assertEquals(
+                "The inputRect is partially outside the limitingRect",
+                expectedRect,
+                DisplayUtil.clampRect(inputRect, limitingRect));
+    }
+
+    @Test
+    public void testClampRect_inputRectPartiallyOutsideLimitingRectInVerticalAxis() {
+        Rect limitingRect = new Rect(0, 0, 1000, 1000);
+        Rect inputRect = new Rect(200, -100, 800, 700);
+
+        // Size can be preserved, there is exactly one Rect with given size distant by 100 px in
+        // Manhattan metric that fits inside the limitingRect, and there is no valid Rect with given
+        // size closer than 100 px.
+        Rect expectedRect = new Rect(200, 0, 800, 800);
+        assertEquals(
+                "The inputRect is partially outside the limitingRect",
+                expectedRect,
+                DisplayUtil.clampRect(inputRect, limitingRect));
+    }
+
+    @Test
+    public void testClampRect_inputRectFullyOutsideLimitingRect() {
+        Rect limitingRect = new Rect(0, 0, 1000, 1000);
+        Rect inputRect = new Rect(1100, 1200, 1600, 1800);
+
+        // Size can be preserved, there is exactly one Rect with given size distant by 1400 px in
+        // Manhattan metric that fits inside the limitingRect, and there is no valid Rect with given
+        // size closer than 1400 px.
+        Rect expectedRect = new Rect(500, 400, 1000, 1000);
+        assertEquals(
+                "The inputRect is fully outside the limitingRect",
+                expectedRect,
+                DisplayUtil.clampRect(inputRect, limitingRect));
+    }
+
+    @Test
+    public void testClampRect_inputRectFullyOutsideAndWiderThanLimitingRect() {
+        Rect limitingRect = new Rect(0, 0, 1100, 1200);
+        Rect inputRect = new Rect(-100, 1400, 1200, 1800);
+
+        // Size cannot be preserved in horizontal axis. The least displacement in the vertical axis
+        // to get a Rect inside the display is 400px.
+        Rect expectedRect = new Rect(0, 800, 1100, 1200);
+        assertEquals(
+                "The inputRect is fully outside and wider than the limitingRect",
+                expectedRect,
+                DisplayUtil.clampRect(inputRect, limitingRect));
+    }
+
+    @Test
+    public void testClampRect_inputRectBiggerThanLimitingRect() {
+        Rect limitingRect = new Rect(0, 0, 1400, 1200);
+        Rect inputRect = new Rect(-100, 1400, 1400, 3000);
+        assertEquals(
+                "The inputRect is bigger in both dimensions than the limitingRect",
+                limitingRect,
+                DisplayUtil.clampRect(inputRect, limitingRect));
+    }
+
+    @Test
+    public void testClampWindowToDisplay() {
         when(mDisplayAndroid.getLocalBounds()).thenReturn(new Rect(0, 0, 1920, 1080));
         final Rect testBounds = new Rect(100, 200, 700, 800);
         assertEquals(
@@ -445,72 +524,6 @@
     }
 
     @Test
-    public void testClampWindowToDisplay_boundsPartiallyOffscreen() {
-        when(mDisplayAndroid.getLocalBounds()).thenReturn(new Rect(0, 0, 1000, 1000));
-        final Rect testBounds = new Rect(-100, 200, 700, 800);
-        // Size can be preserved, there is exactly one Rect with given size distant by 100 px in
-        // Manhattan metric that fits inside the display, and there is no valid Rect with given size
-        // closer than 100 px.
-        final Rect expectedBounds = new Rect(0, 200, 800, 800);
-        assertEquals(
-                "The bounds were partially off-screen",
-                expectedBounds,
-                DisplayUtil.clampWindowToDisplay(testBounds, mDisplayAndroid));
-    }
-
-    @Test
-    public void testClampWindowToDisplay_boundsPartiallyOffscreen2() {
-        when(mDisplayAndroid.getLocalBounds()).thenReturn(new Rect(0, 0, 1000, 1000));
-        final Rect testBounds = new Rect(200, -100, 800, 700);
-        // Size can be preserved, there is exactly one Rect with given size distant by 100 px in
-        // Manhattan metric that fits inside the display, and there is no valid Rect with given size
-        // closer than 100 px.
-        final Rect expectedBounds = new Rect(200, 0, 800, 800);
-        assertEquals(
-                "The bounds were partially off-screen",
-                expectedBounds,
-                DisplayUtil.clampWindowToDisplay(testBounds, mDisplayAndroid));
-    }
-
-    @Test
-    public void testClampWindowToDisplay_boundsFullyOffscreen() {
-        when(mDisplayAndroid.getLocalBounds()).thenReturn(new Rect(0, 0, 1000, 1000));
-        final Rect testBounds = new Rect(1100, 1200, 1600, 1800);
-        // Size can be preserved, there is exactly one Rect with given size distant by 1400 px in
-        // Manhattan metric that fits inside the display, and there is no valid Rect with given size
-        // closer than 1400 px.
-        final Rect expectedBounds = new Rect(500, 400, 1000, 1000);
-        assertEquals(
-                "The bounds were fully off-screen",
-                expectedBounds,
-                DisplayUtil.clampWindowToDisplay(testBounds, mDisplayAndroid));
-    }
-
-    @Test
-    public void testClampWindowToDisplay_boundsFullyOffscreenAndWiderThanDisplay() {
-        when(mDisplayAndroid.getLocalBounds()).thenReturn(new Rect(0, 0, 1100, 1200));
-        final Rect testBounds = new Rect(-100, 1400, 1200, 1800);
-        // Size cannot be preserved in horizontal axis. The least displacement in the vertical axis
-        // to get a Rect inside the display is 400px.
-        final Rect expectedBounds = new Rect(0, 800, 1100, 1200);
-        assertEquals(
-                "The bounds were fully off-screen and wider than the display",
-                expectedBounds,
-                DisplayUtil.clampWindowToDisplay(testBounds, mDisplayAndroid));
-    }
-
-    @Test
-    public void testClampWindowToDisplay_boundsBiggerThanDisplay() {
-        final Rect displayLocalBounds = new Rect(0, 0, 1400, 1200);
-        when(mDisplayAndroid.getLocalBounds()).thenReturn(displayLocalBounds);
-        final Rect testBounds = new Rect(-100, 1400, 1400, 3000);
-        assertEquals(
-                "The bounds were bigger in both dimensions than the display",
-                displayLocalBounds,
-                DisplayUtil.clampWindowToDisplay(testBounds, mDisplayAndroid));
-    }
-
-    @Test
     public void scaleToEnclosingRect() {
         Rect rect = new Rect(-10, -20, 10, 20);
 
diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn
index ca9b2b6..41572ec 100644
--- a/ui/display/BUILD.gn
+++ b/ui/display/BUILD.gn
@@ -124,6 +124,7 @@
     "//base",
     "//base:i18n",
     "//components/device_event_log",
+    "//components/viz/common/resources:shared_image_format",
     "//mojo/public/cpp/bindings:struct_traits",
     "//ui/display/mojom:mojom_shared_cpp_sources",
     "//ui/display/util",
@@ -311,6 +312,7 @@
     "//base",
     "//base/test:test_support",
     "//cc/base",
+    "//components/viz/common/resources:shared_image_format",
     "//mojo/public/cpp/test_support:test_utils",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/ui/display/mac/DEPS b/ui/display/mac/DEPS
index 4a3d7376..d53cf4b 100644
--- a/ui/display/mac/DEPS
+++ b/ui/display/mac/DEPS
@@ -1,3 +1,7 @@
+include_rules = [
+  "+components/viz/common/resources/shared_image_format.h",
+]
+
 specific_include_rules = {
   "screen_mac_headless\.mm": [
     "+components/headless/screen_info",
diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm
index 4be0d350..0b1dfdb 100644
--- a/ui/display/mac/screen_mac.mm
+++ b/ui/display/mac/screen_mac.mm
@@ -33,6 +33,7 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "components/device_event_log/device_event_log.h"
+#include "components/viz/common/resources/shared_image_format.h"
 #include "ui/display/display.h"
 #include "ui/display/display_change_notifier.h"
 #include "ui/display/mac/screen_mac_headless.h"
@@ -164,9 +165,10 @@
     if (enable_hdr) {
       bool needs_alpha_values[] = {true, false};
       for (const auto& needs_alpha : needs_alpha_values) {
-        display_color_spaces.SetOutputColorSpaceAndBufferFormat(
+        display_color_spaces.SetOutputColorSpaceAndFormat(
             gfx::ContentColorUsage::kHDR, needs_alpha,
-            gfx::ColorSpace::CreateExtendedSRGB(), gfx::BufferFormat::RGBA_F16);
+            gfx::ColorSpace::CreateExtendedSRGB(),
+            viz::SinglePlaneFormat::kRGBA_F16);
       }
       display_color_spaces.SetHDRMaxLuminanceRelative(hdr_max_lum_relative);
     }
diff --git a/ui/display/manager/BUILD.gn b/ui/display/manager/BUILD.gn
index c59e6e0d..35169f68 100644
--- a/ui/display/manager/BUILD.gn
+++ b/ui/display/manager/BUILD.gn
@@ -63,6 +63,7 @@
     "//chromeos/constants",
     "//chromeos/ui/base",
     "//components/device_event_log",
+    "//components/viz/common/resources:shared_image_format",
     "//third_party/re2",
     "//ui/base",
     "//ui/display/mojom",
diff --git a/ui/display/manager/DEPS b/ui/display/manager/DEPS
index ac8c5f3..8f372a8 100644
--- a/ui/display/manager/DEPS
+++ b/ui/display/manager/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/viz/common/resources/shared_image_format.h",
   "+chromeos",
   "+third_party/skia",
   "+ui/base",
diff --git a/ui/display/manager/managed_display_info.cc b/ui/display/manager/managed_display_info.cc
index 5accdb0f..92d2a7a 100644
--- a/ui/display/manager/managed_display_info.cc
+++ b/ui/display/manager/managed_display_info.cc
@@ -22,6 +22,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
+#include "components/viz/common/resources/shared_image_format.h"
 #include "ui/display/display.h"
 #include "ui/display/display_features.h"
 #include "ui/display/display_switches.h"
@@ -343,7 +344,7 @@
 
   if (has_hdr) {
     gfx::DisplayColorSpaces display_color_spaces{
-        gfx::ColorSpace::CreateHDR10(), gfx::BufferFormat::BGRA_1010102};
+        gfx::ColorSpace::CreateHDR10(), viz::SinglePlaneFormat::kBGRA_1010102};
     display_info.set_display_color_spaces(display_color_spaces);
   }
 
diff --git a/ui/display/manager/managed_display_info_unittest.cc b/ui/display/manager/managed_display_info_unittest.cc
index 7b708ac5..a12658e 100644
--- a/ui/display/manager/managed_display_info_unittest.cc
+++ b/ui/display/manager/managed_display_info_unittest.cc
@@ -5,16 +5,16 @@
 #include "ui/display/manager/managed_display_info.h"
 
 #include "base/test/gtest_util.h"
+#include "components/viz/common/resources/shared_image_format.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/display_switches.h"
 #include "ui/display/manager/display_change_observer.h"
 #include "ui/display/manager/test/fake_display_snapshot.h"
+#include "ui/display/manager/touch_device_manager.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/gfx/display_color_spaces.h"
 #include "ui/gfx/geometry/rounded_corners_f.h"
 
-#include "ui/display/manager/touch_device_manager.h"
-
 namespace display {
 
 using DisplayInfoTest = testing::Test;
@@ -43,7 +43,7 @@
   EXPECT_EQ(gfx::Size(288, 380), info.size_in_pixel());
   EXPECT_EQ(Display::ROTATE_0, info.GetActiveRotation());
   EXPECT_EQ(gfx::DisplayColorSpaces(gfx::ColorSpace::CreateHDR10(),
-                                    gfx::BufferFormat::BGRA_1010102),
+                                    viz::SinglePlaneFormat::kBGRA_1010102),
             info.display_color_spaces());
   EXPECT_EQ(gfx::Insets::TLBR(5, 3, 5, 3), info.overscan_insets_in_dip());
 
diff --git a/ui/display/util/BUILD.gn b/ui/display/util/BUILD.gn
index cfb992f..2cfbccd 100644
--- a/ui/display/util/BUILD.gn
+++ b/ui/display/util/BUILD.gn
@@ -24,6 +24,7 @@
   deps = [
     "//base",
     "//components/device_event_log",
+    "//components/viz/common/resources:shared_image_format",
     "//skia",
     "//ui/display/types",
     "//ui/gfx:color_space",
diff --git a/ui/display/util/DEPS b/ui/display/util/DEPS
index 81ae4b9..d29c446 100644
--- a/ui/display/util/DEPS
+++ b/ui/display/util/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+third_party/skia/include/core/SkColorSpace.h",
   "+components/device_event_log/device_event_log.h",
+  "+components/viz/common/resources/shared_image_format.h",
 ]
diff --git a/ui/display/util/display_util.cc b/ui/display/util/display_util.cc
index 0029ef43..b905fbd0 100644
--- a/ui/display/util/display_util.cc
+++ b/ui/display/util/display_util.cc
@@ -16,6 +16,7 @@
 #include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "components/device_event_log/device_event_log.h"
+#include "components/viz/common/resources/shared_image_format.h"
 #include "ui/display/types/display_snapshot.h"
 #include "ui/display/util/edid_parser.h"
 #include "ui/gfx/icc_profile.h"
@@ -342,12 +343,12 @@
     gfx::ColorSpace hdr_color_space = gfx::ColorSpace::CreateCustom(
         primary_matrix, gfx::ColorSpace::TransferID::SRGB_HDR);
 
-    display_color_spaces.SetOutputColorSpaceAndBufferFormat(
+    display_color_spaces.SetOutputColorSpaceAndFormat(
         gfx::ContentColorUsage::kHDR, false /* needs_alpha */, hdr_color_space,
-        gfx::BufferFormat::RGBA_1010102);
-    display_color_spaces.SetOutputColorSpaceAndBufferFormat(
+        viz::SinglePlaneFormat::kRGBA_1010102);
+    display_color_spaces.SetOutputColorSpaceAndFormat(
         gfx::ContentColorUsage::kHDR, true /* needs_alpha */, hdr_color_space,
-        gfx::BufferFormat::RGBA_1010102);
+        viz::SinglePlaneFormat::kRGBA_1010102);
     display_color_spaces.SetHDRMaxLuminanceRelative(1.1f);
   }
 
@@ -360,7 +361,7 @@
     // ContentColorUsage. BT2020 primaries and PQ transfer function require a
     // 10-bit buffer.
     display_color_spaces = gfx::DisplayColorSpaces(
-        gfx::ColorSpace::CreateHDR10(), gfx::BufferFormat::RGBA_1010102);
+        gfx::ColorSpace::CreateHDR10(), viz::SinglePlaneFormat::kRGBA_1010102);
     // TODO(b/165822222): Set initial luminance values based on display
     // brightness
     display_color_spaces.SetSDRMaxLuminanceNits(
diff --git a/ui/display/win/DEPS b/ui/display/win/DEPS
index 8371ff6..2b93c09 100644
--- a/ui/display/win/DEPS
+++ b/ui/display/win/DEPS
@@ -1,3 +1,7 @@
+include_rules = [
+  "+components/viz/common/resources/shared_image_format.h",
+]
+
 specific_include_rules = {
     "screen_win_headless.*\.cc": [
       "+components/headless/screen_info"
diff --git a/ui/display/win/screen_win.cc b/ui/display/win/screen_win.cc
index 5ec97763..85b1088 100644
--- a/ui/display/win/screen_win.cc
+++ b/ui/display/win/screen_win.cc
@@ -29,6 +29,7 @@
 #include "base/win/win_util.h"
 #include "base/win/windows_version.h"
 #include "components/device_event_log/device_event_log.h"
+#include "components/viz/common/resources/shared_image_format.h"
 #include "ui/display/display.h"
 #include "ui/display/display_features.h"
 #include "ui/display/display_layout.h"
@@ -233,15 +234,15 @@
     // Windows RS3, but RGB10A2 with HDR10 color space works fine (see
     // https://crbug.com/937108#c92).
     if (base::win::GetVersion() > base::win::Version::WIN10_RS3) {
-      color_spaces.SetOutputColorSpaceAndBufferFormat(
-          usage, !kNeedsAlpha, scrgb_linear, gfx::BufferFormat::RGBA_F16);
+      color_spaces.SetOutputColorSpaceAndFormat(
+          usage, !kNeedsAlpha, scrgb_linear, viz::SinglePlaneFormat::kRGBA_F16);
     } else {
-      color_spaces.SetOutputColorSpaceAndBufferFormat(
-          usage, !kNeedsAlpha, hdr10, gfx::BufferFormat::RGBA_1010102);
+      color_spaces.SetOutputColorSpaceAndFormat(
+          usage, !kNeedsAlpha, hdr10, viz::SinglePlaneFormat::kRGBA_1010102);
     }
     // Use RGBA F16 backbuffers for HDR if alpha channel is required.
-    color_spaces.SetOutputColorSpaceAndBufferFormat(
-        usage, kNeedsAlpha, scrgb_linear, gfx::BufferFormat::RGBA_F16);
+    color_spaces.SetOutputColorSpaceAndFormat(
+        usage, kNeedsAlpha, scrgb_linear, viz::SinglePlaneFormat::kRGBA_F16);
   }
   return color_spaces;
 }
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index a00134fb..a3c131d3 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -683,6 +683,8 @@
       "native_pixmap_handle.cc",
       "native_pixmap_handle.h",
     ]
+
+    deps += [ "//components/viz/common/resources:shared_image_format" ]
   }
 
   if (enable_vulkan) {
diff --git a/ui/gfx/display_color_spaces.cc b/ui/gfx/display_color_spaces.cc
index d86b673..18f3744 100644
--- a/ui/gfx/display_color_spaces.cc
+++ b/ui/gfx/display_color_spaces.cc
@@ -75,17 +75,14 @@
     color_spaces_[i] = c;
 }
 
-DisplayColorSpaces::DisplayColorSpaces(const ColorSpace& c, BufferFormat f)
-    : DisplayColorSpaces(c) {
-  for (size_t i = 0; i < kConfigCount; i++) {
-    buffer_formats_[i] = f;
-  }
-}
-
 DisplayColorSpaces::DisplayColorSpaces(const ColorSpace& c,
                                        viz::SharedImageFormat f)
-    : DisplayColorSpaces(c,
-                         viz::SinglePlaneSharedImageFormatToBufferFormat(f)) {}
+    : DisplayColorSpaces(c) {
+  auto buffer_format = viz::SinglePlaneSharedImageFormatToBufferFormat(f);
+  for (size_t i = 0; i < kConfigCount; i++) {
+    buffer_formats_[i] = buffer_format;
+  }
+}
 
 void DisplayColorSpaces::SetOutputBufferFormats(
     gfx::BufferFormat buffer_format_no_alpha,
@@ -98,14 +95,22 @@
   }
 }
 
-void DisplayColorSpaces::SetOutputColorSpaceAndBufferFormat(
+void DisplayColorSpaces::SetOutputFormats(
+    viz::SharedImageFormat format_no_alpha,
+    viz::SharedImageFormat format_with_alpha) {
+  SetOutputBufferFormats(
+      viz::SinglePlaneSharedImageFormatToBufferFormat(format_no_alpha),
+      viz::SinglePlaneSharedImageFormatToBufferFormat(format_with_alpha));
+}
+
+void DisplayColorSpaces::SetOutputColorSpaceAndFormat(
     ContentColorUsage color_usage,
     bool needs_alpha,
     const gfx::ColorSpace& color_space,
-    gfx::BufferFormat buffer_format) {
+    viz::SharedImageFormat format) {
   size_t i = GetIndex(color_usage, needs_alpha);
   color_spaces_[i] = color_space;
-  buffer_formats_[i] = buffer_format;
+  buffer_formats_[i] = viz::SinglePlaneSharedImageFormatToBufferFormat(format);
 }
 
 ColorSpace DisplayColorSpaces::GetOutputColorSpace(
diff --git a/ui/gfx/display_color_spaces.h b/ui/gfx/display_color_spaces.h
index 3232b0c..56b02829 100644
--- a/ui/gfx/display_color_spaces.h
+++ b/ui/gfx/display_color_spaces.h
@@ -57,29 +57,29 @@
   // build configuration.
   explicit DisplayColorSpaces(const ColorSpace& color_space);
 
-  // Initialize as |color_space| and |buffer_format| for all settings. If
-  // |color_space| is the default (invalid) color space, then initialize to
-  // sRGB.
-  DisplayColorSpaces(const ColorSpace& color_space, BufferFormat buffer_format);
-
   // Initialize as |color_space| and |format| (which must be single-plane) for
   // all settings. If |color_space| is the default (invalid) color space, then
   // initialize to sRGB.
   DisplayColorSpaces(const ColorSpace& color_space,
                      viz::SharedImageFormat format);
 
-  // Set the color space and buffer format for the final output surface when the
-  // specified content is being displayed.
-  void SetOutputColorSpaceAndBufferFormat(ContentColorUsage color_usage,
-                                          bool needs_alpha,
-                                          const gfx::ColorSpace& color_space,
-                                          gfx::BufferFormat buffer_format);
+  // Set the color space and SharedImageFormat for the final output surface when
+  // the specified content is being displayed.
+  void SetOutputColorSpaceAndFormat(ContentColorUsage color_usage,
+                                    bool needs_alpha,
+                                    const gfx::ColorSpace& color_space,
+                                    viz::SharedImageFormat format);
 
   // Set the buffer format for all color usages to |buffer_format_no_alpha| when
   // alpha is not needed and |buffer_format_with_alpha| when alpha is needed.
   void SetOutputBufferFormats(gfx::BufferFormat buffer_format_no_alpha,
                               gfx::BufferFormat buffer_format_with_alpha);
 
+  // Set the format for all color usages to |format_no_alpha| when alpha is not
+  // needed and |format_with_alpha| when alpha is needed.
+  void SetOutputFormats(viz::SharedImageFormat format_no_alpha,
+                        viz::SharedImageFormat format_with_alpha);
+
   // Retrieve parameters for a specific usage and alpha.
   ColorSpace GetOutputColorSpace(ContentColorUsage color_usage,
                                  bool needs_alpha) const;
diff --git a/ui/gfx/native_pixmap_handle.cc b/ui/gfx/native_pixmap_handle.cc
index a27f45bd..3865b84a 100644
--- a/ui/gfx/native_pixmap_handle.cc
+++ b/ui/gfx/native_pixmap_handle.cc
@@ -8,6 +8,7 @@
 
 #include "base/logging.h"
 #include "build/build_config.h"
+#include "components/viz/common/resources/shared_image_format_utils.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -137,9 +138,10 @@
 
 bool CanFitImageForSizeAndFormat(const gfx::NativePixmapHandle& handle,
                                  const gfx::Size& size,
-                                 gfx::BufferFormat format,
+                                 gfx::BufferFormat buffer_format,
                                  bool assume_single_memory_object) {
-  size_t expected_planes = gfx::NumberOfPlanesForLinearBufferFormat(format);
+  viz::SharedImageFormat format = viz::GetSharedImageFormat(buffer_format);
+  size_t expected_planes = format.NumberOfPlanes();
   if (expected_planes == 0 || handle.planes.size() != expected_planes)
     return false;
 
@@ -162,18 +164,15 @@
   for (size_t i = 0; i < handle.planes.size(); ++i) {
     const size_t plane_stride =
         base::strict_cast<size_t>(handle.planes[i].stride);
-    size_t min_stride = 0;
-    if (!gfx::RowSizeForBufferFormatChecked(
-            base::checked_cast<size_t>(size.width()), format, i, &min_stride) ||
-        plane_stride < min_stride) {
+
+    auto min_stride = viz::SharedMemoryRowSizeForSharedImageFormat(
+        format, i, base::checked_cast<size_t>(size.width()));
+    if (!min_stride || plane_stride < min_stride.value()) {
       return false;
     }
 
-    const size_t subsample_factor = SubsamplingFactorForBufferFormat(format, i);
     const base::CheckedNumeric<size_t> plane_height =
-        base::CheckDiv(base::CheckAdd(base::checked_cast<size_t>(size.height()),
-                                      base::CheckSub(subsample_factor, 1)),
-                       subsample_factor);
+        format.GetPlaneSize(i, size).height();
     const base::CheckedNumeric<size_t> min_size = plane_height * plane_stride;
     if (!min_size.IsValid<uint64_t>() ||
         handle.planes[i].size < min_size.ValueOrDie<uint64_t>()) {
diff --git a/ui/native_theme/os_settings_provider_win.cc b/ui/native_theme/os_settings_provider_win.cc
index 264d68639..38a1eff 100644
--- a/ui/native_theme/os_settings_provider_win.cc
+++ b/ui/native_theme/os_settings_provider_win.cc
@@ -25,6 +25,18 @@
 
 namespace ui {
 
+namespace {
+
+bool IsSystemForcedColorsActive() {
+  if (HIGHCONTRAST result = {.cbSize = sizeof(HIGHCONTRAST)};
+      SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0)) {
+    return !!(result.dwFlags & HCF_HIGHCONTRASTON);
+  }
+  return false;
+}
+
+}  // namespace
+
 OsSettingsProviderWin::OsSettingsProviderWin()
     : OsSettingsProvider(PriorityLevel::kProduction) {
   // If there's no sequenced task runner handle, we can't be called back for
@@ -52,6 +64,9 @@
   }
   UpdateColors();
 
+  // Initialize forced colors (high contrast) state.
+  forced_colors_active_ = IsSystemForcedColorsActive();
+
   // Histogram high contrast state.
   // NOTE: Reported in metrics; do not reorder, add additional values at end.
   enum class HighContrastColorScheme {
@@ -235,13 +250,10 @@
       NotifyOnSettingsChanged(true);
     }
   } else if (message == WM_SETTINGCHANGE && wparam == SPI_SETHIGHCONTRAST) {
-    if (HIGHCONTRAST result = {.cbSize = sizeof(HIGHCONTRAST)};
-        SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0)) {
-      const bool old_forced_colors_active = ForcedColorsActive();
-      forced_colors_active_ = !!(result.dwFlags & HCF_HIGHCONTRASTON);
-      if (ForcedColorsActive() != old_forced_colors_active) {
-        NotifyOnSettingsChanged();
-      }
+    const bool old_forced_colors_active = ForcedColorsActive();
+    forced_colors_active_ = IsSystemForcedColorsActive();
+    if (ForcedColorsActive() != old_forced_colors_active) {
+      NotifyOnSettingsChanged();
     }
   }
 }
diff --git a/ui/ozone/platform/wayland/host/wayland_keyboard.cc b/ui/ozone/platform/wayland/host/wayland_keyboard.cc
index adb1aa3d..3726dd2 100644
--- a/ui/ozone/platform/wayland/host/wayland_keyboard.cc
+++ b/ui/ozone/platform/wayland/host/wayland_keyboard.cc
@@ -396,7 +396,15 @@
                                  uint32_t key,
                                  uint32_t state,
                                  KeyEventKind kind) {
-  bool down = state == WL_KEYBOARD_KEY_STATE_PRESSED;
+  // If we receive a Repeat event while repeat info != 0,
+  // we shouldn't dispatch it.
+  if (state == WL_KEYBOARD_KEY_STATE_REPEATED &&
+      auto_repeat_handler_.IsAutoRepeatEnabled()) {
+    LOG(WARNING) << "Received key repeat event while repeat rate is non-zero";
+    return;
+  }
+
+  bool down = state != WL_KEYBOARD_KEY_STATE_RELEASED;
   if (down) {
     connection_->serial_tracker().UpdateSerial(wl::SerialType::kKeyPress,
                                                serial);
@@ -418,9 +426,11 @@
     return;
   }
 
-  DispatchKey(
-      key, 0 /*scan_code*/, down, false /*repeat*/, std::make_optional(serial),
-      wl::EventMillisecondsToTimeTicks(time), device_id(), EF_NONE, kind);
+  DispatchKey(key, 0 /*scan_code*/, down,
+              state == WL_KEYBOARD_KEY_STATE_REPEATED /*repeat*/,
+              std::make_optional(serial),
+              wl::EventMillisecondsToTimeTicks(time), device_id(), EF_NONE,
+              kind);
 }
 
 void WaylandKeyboard::DispatchKey(unsigned int key,
diff --git a/ui/ozone/platform/wayland/host/wayland_pointer.cc b/ui/ozone/platform/wayland/host/wayland_pointer.cc
index 4d20b546..1a28002 100644
--- a/ui/ozone/platform/wayland/host/wayland_pointer.cc
+++ b/ui/ozone/platform/wayland/host/wayland_pointer.cc
@@ -50,6 +50,7 @@
       .axis_stop = &OnAxisStop,
       .axis_discrete = &OnAxisDiscrete,
       .axis_value120 = &OnAxisValue120,
+      .axis_relative_direction = &OnAxisRelativeDirection,
   };
   wl_pointer_add_listener(obj_.get(), &kPointerListener, this);
 }
@@ -210,8 +211,7 @@
                                     wl_pointer* pointer,
                                     uint32_t axis,
                                     int32_t discrete) {
-  // TODO(crbug.com/40720099): Use this event for better handling of mouse wheel
-  // events.
+  // Deprecated since version 8
   NOTIMPLEMENTED_LOG_ONCE();
 }
 
@@ -250,4 +250,14 @@
   }
 }
 
+// --- Version 9 ---
+
+// static
+void WaylandPointer::OnAxisRelativeDirection(void* data,
+                                             wl_pointer* obj,
+                                             uint32_t axis,
+                                             uint32_t direction) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_pointer.h b/ui/ozone/platform/wayland/host/wayland_pointer.h
index 8e84872..7b1b001 100644
--- a/ui/ozone/platform/wayland/host/wayland_pointer.h
+++ b/ui/ozone/platform/wayland/host/wayland_pointer.h
@@ -97,6 +97,11 @@
                   std::optional<base::TimeTicks> timestamp,
                   bool is_high_resolution);
 
+  static void OnAxisRelativeDirection(void* data,
+                                      wl_pointer* obj,
+                                      uint32_t axis,
+                                      uint32_t direction);
+
   wl::Object<wl_pointer> obj_;
   const raw_ptr<WaylandConnection> connection_;
   const raw_ptr<Delegate> delegate_;
diff --git a/ui/ozone/platform/wayland/host/wayland_seat.cc b/ui/ozone/platform/wayland/host/wayland_seat.cc
index 36670d9..0fa39bc 100644
--- a/ui/ozone/platform/wayland/host/wayland_seat.cc
+++ b/ui/ozone/platform/wayland/host/wayland_seat.cc
@@ -19,7 +19,7 @@
 
 namespace {
 constexpr uint32_t kMinVersion = 1;
-constexpr uint32_t kMaxVersion = 8;
+constexpr uint32_t kMaxVersion = 10;
 }  // namespace
 
 // static
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
index 82d16e0..6437cf1 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -135,8 +135,11 @@
 
   UpdateWindowScale(false);
 
-  if (inactive)
+  if (inactive) {
     Deactivate();
+  } else {
+    Activate();
+  }
 
   WaylandWindow::Show(inactive);
 }
@@ -292,17 +295,20 @@
 
 void WaylandToplevelWindow::ActivateWithToken(std::string token) {
   DCHECK(connection()->xdg_activation());
-  bool can_activate = IsSurfaceConfigured();
 
   // Stacking the dragged xdg toplevel as the topmost one (and tied to the
   // pointer cursor) is reponsibility of the Wayland compositor, so bail out
   // if `this` is currently being dragged.
   if (auto* drag_controller = connection()->window_drag_controller()) {
-    can_activate &= !drag_controller->IsDraggingWindow(this);
+    if (drag_controller->IsDraggingWindow(this)) {
+      return;
+    }
   }
 
-  if (can_activate) {
+  if (IsSurfaceConfigured()) {
     connection()->xdg_activation()->Activate(root_surface()->surface(), token);
+  } else {
+    pending_configure_activation_token_ = token;
   }
 }
 
@@ -665,6 +671,13 @@
   if (xdg_toplevel()) {
     xdg_toplevel()->AckConfigure(serial);
   }
+
+  if (pending_configure_activation_token_.has_value()) {
+    DCHECK(connection()->xdg_activation());
+    connection()->xdg_activation()->Activate(
+        root_surface()->surface(), pending_configure_activation_token_.value());
+    pending_configure_activation_token_.reset();
+  }
 }
 
 base::WeakPtr<WaylandWindow> WaylandToplevelWindow::AsWeakPtr() {
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
index a7a091c..eb8c4f5 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
@@ -249,6 +249,10 @@
   // The z order for the window.
   ZOrderLevel z_order_ = ZOrderLevel::kNormal;
 
+  // Activation will only be done if the surface is configured.
+  // If it is requested before, it is stored and executed in ack configure.
+  std::optional<std::string> pending_configure_activation_token_ = std::nullopt;
+
   raw_ptr<WorkspaceExtensionDelegate> workspace_extension_delegate_ = nullptr;
 
   gfx::ImageSkia initial_icon_;
diff --git a/ui/ozone/platform/wayland/host/wayland_wp_color_management_surface.cc b/ui/ozone/platform/wayland/host/wayland_wp_color_management_surface.cc
index 9a31d99b..fa754b6 100644
--- a/ui/ozone/platform/wayland/host/wayland_wp_color_management_surface.cc
+++ b/ui/ozone/platform/wayland/host/wayland_wp_color_management_surface.cc
@@ -99,6 +99,14 @@
   CHECK(self);
   CHECK_EQ(feedback_surface, self->feedback_surface_.get());
 
+  // Reset the previous image description before creating a new one.
+  // Per the color management protocol specification:
+  // "The client should stop using and destroy the image descriptions created
+  // by earlier invocations of this request for the associated wl_surface."
+  // This prevents "invalid object" errors when the compositor or client tries
+  // to reference the old object ID after it has been destroyed.
+  self->image_description_.reset();
+
   auto image_description_object = wl::Object<wp_image_description_v1>(
       wp_color_management_surface_feedback_v1_get_preferred(feedback_surface));
 
diff --git a/ui/ozone/platform/wayland/host/xdg_activation_unittest.cc b/ui/ozone/platform/wayland/host/xdg_activation_unittest.cc
index e94ec7f6..d113475 100644
--- a/ui/ozone/platform/wayland/host/xdg_activation_unittest.cc
+++ b/ui/ozone/platform/wayland/host/xdg_activation_unittest.cc
@@ -43,10 +43,12 @@
 
   window_.reset();
 
-  auto window1 = CreateWaylandWindowWithParams(PlatformWindowType::kWindow,
-                                               kDefaultBounds, &delegate);
-  auto window2 = CreateWaylandWindowWithParams(PlatformWindowType::kWindow,
-                                               kDefaultBounds, &delegate);
+  auto window1 = CreateWaylandWindowWithParams(
+      PlatformWindowType::kWindow, kDefaultBounds, &delegate,
+      gfx::kNullAcceleratedWidget, true /*inactive*/);
+  auto window2 = CreateWaylandWindowWithParams(
+      PlatformWindowType::kWindow, kDefaultBounds, &delegate,
+      gfx::kNullAcceleratedWidget, true /*inactive*/);
 
   // When window is shown, it automatically gets keyboard focus. Reset it
   connection_->window_manager()->SetKeyboardFocusedWindow(nullptr);
@@ -102,6 +104,23 @@
   }
 }
 
+// Tests that showing a new window automatically requests a token.
+TEST_F(XdgActivationTest, RequestOnShow) {
+  MockWaylandPlatformWindowDelegate delegate(connection_.get());
+
+  auto window = CreateWaylandWindowWithParams(PlatformWindowType::kWindow,
+                                              kDefaultBounds, &delegate);
+  PostToServerAndWait(
+      [](wl::TestWaylandServerThread* server) {
+        auto* const xdg_activation = server->xdg_activation_v1();
+        ASSERT_TRUE(xdg_activation);
+        ASSERT_TRUE(xdg_activation->get_token());
+        xdg_activation_token_v1_send_done(
+            xdg_activation->get_token()->resource(), kMockStaticTestToken);
+      },
+      true);
+}
+
 // Tests that with too many requests at some point the request queue will be
 // full and the subsequent request callbacks will be run immediately with an
 // empty token instead of adding them to the queue for sending to the server
diff --git a/ui/ozone/platform/wayland/test/wayland_test.cc b/ui/ozone/platform/wayland/test/wayland_test.cc
index 551b9172..efe91ee7 100644
--- a/ui/ozone/platform/wayland/test/wayland_test.cc
+++ b/ui/ozone/platform/wayland/test/wayland_test.cc
@@ -250,7 +250,8 @@
     PlatformWindowType type,
     const gfx::Rect bounds,
     MockWaylandPlatformWindowDelegate* delegate,
-    gfx::AcceleratedWidget parent_widget) {
+    gfx::AcceleratedWidget parent_widget,
+    bool inactive) {
   PlatformWindowInitProperties properties;
   properties.bounds = bounds;
   properties.type = type;
@@ -259,7 +260,7 @@
   auto window =
       delegate->CreateWaylandWindow(connection_.get(), std::move(properties));
   if (window)
-    window->Show(false);
+    window->Show(inactive);
   return window;
 }
 
diff --git a/ui/ozone/platform/wayland/test/wayland_test.h b/ui/ozone/platform/wayland/test/wayland_test.h
index bf6786e..7cd7da6 100644
--- a/ui/ozone/platform/wayland/test/wayland_test.h
+++ b/ui/ozone/platform/wayland/test/wayland_test.h
@@ -110,7 +110,8 @@
       PlatformWindowType type,
       const gfx::Rect bounds,
       MockWaylandPlatformWindowDelegate* delegate,
-      gfx::AcceleratedWidget parent_widget = gfx::kNullAcceleratedWidget);
+      gfx::AcceleratedWidget parent_widget = gfx::kNullAcceleratedWidget,
+      bool inactive = false);
 
   base::test::TaskEnvironment task_environment_;
 
diff --git a/ui/views/animation/ink_drop_util.cc b/ui/views/animation/ink_drop_util.cc
index 6817b64b..cd9f90b 100644
--- a/ui/views/animation/ink_drop_util.cc
+++ b/ui/views/animation/ink_drop_util.cc
@@ -12,7 +12,6 @@
 #include "ui/gfx/geometry/vector2d_f.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/view.h"
-#include "ui/views/views_features.h"
 
 namespace views {
 
@@ -63,9 +62,7 @@
   return native_theme->forced_colors() !=
              ui::ColorProviderKey::ForcedColors::kNone &&
          native_theme->preferred_contrast() ==
-             ui::NativeTheme::PreferredContrast::kMore &&
-         base::FeatureList::IsEnabled(
-             features::kEnablePlatformHighContrastInkDrop);
+             ui::NativeTheme::PreferredContrast::kMore;
 }
 
 }  // namespace views
diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
index 240bf8c..ac425721 100644
--- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm
+++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
@@ -1703,7 +1703,8 @@
 void NativeWidgetMacNSWindowHost::GetHitTestResult(
     const gfx::Point& location_in_content,
     GetHitTestResultCallback callback) {
-  remote_cocoa::mojom::HitTestResult hit_test_result;
+  remote_cocoa::mojom::HitTestResult hit_test_result =
+      remote_cocoa::mojom::HitTestResult::kOther;
   GetHitTestResult(location_in_content, &hit_test_result);
   std::move(callback).Run(hit_test_result);
 }
diff --git a/ui/views/views_features.cc b/ui/views/views_features.cc
index 88ca7f0..eddb6b3 100644
--- a/ui/views/views_features.cc
+++ b/ui/views/views_features.cc
@@ -15,11 +15,6 @@
 BASE_FEATURE(kAnnounceTextAdditionalAttributes,
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Use a high-contrast style for ink drops when in platform high-contrast mode,
-// including full opacity and a high-contrast color
-BASE_FEATURE(kEnablePlatformHighContrastInkDrop,
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 // If mouse cursor is over different window Windows will not start a Drag
 // and drop. This feature moves the cursor to the location of a touch on
 // press-down so that by the time a Drag and drop is started, the cursor will
diff --git a/ui/views/views_features.h b/ui/views/views_features.h
index 58063f24..c1d8107 100644
--- a/ui/views/views_features.h
+++ b/ui/views/views_features.h
@@ -13,7 +13,6 @@
 
 // Please keep alphabetized.
 VIEWS_EXPORT BASE_DECLARE_FEATURE(kAnnounceTextAdditionalAttributes);
-VIEWS_EXPORT BASE_DECLARE_FEATURE(kEnablePlatformHighContrastInkDrop);
 VIEWS_EXPORT BASE_DECLARE_FEATURE(kEnableTouchDragCursorSync);
 VIEWS_EXPORT BASE_DECLARE_FEATURE(kKeyboardAccessibleTooltipInViews);
 
diff --git a/ui/webui/resources/cr_components/composebox/context_menu_entrypoint.html.ts b/ui/webui/resources/cr_components/composebox/context_menu_entrypoint.html.ts
index c91136a..1e9683e 100644
--- a/ui/webui/resources/cr_components/composebox/context_menu_entrypoint.html.ts
+++ b/ui/webui/resources/cr_components/composebox/context_menu_entrypoint.html.ts
@@ -34,12 +34,12 @@
           <button class="dropdown-item"
               title="${tab.title}" data-index="${index}"
               @pointerenter="${this.onTabPointerenter_}"
-              @click="${this.addTabContext}">
+              @click="${this.addTabContext_}">
             <composebox-tab-favicon .url="${tab.url.url}">
             </composebox-tab-favicon>
             <span class="tab-title">${tab.title}</span>
           </button>
-          ${this.shouldShowTabPreview() ? html`
+          ${this.shouldShowTabPreview_() ? html`
             <img class="tab-preview" .src="${this.tabPreviewUrl_}">
           ` : ''}
         </div>
@@ -47,13 +47,13 @@
       <hr/>
     `: ''}
     <button id="imageUpload" class="dropdown-item"
-        @click="${this.openImageUpload}">
+        @click="${this.openImageUpload_}">
       <cr-icon icon="composebox:imageUpload"></cr-icon>
       ${this.i18n('addImage')}
     </button>
     <button id="fileUpload" class="dropdown-item"
-        @click="${this.openFileUpload}"
-        ?disabled="${this.inCreateImageMode}">
+        @click="${this.openFileUpload_}"
+        ?disabled="${this.inCreateImageMode_}">
       <cr-icon icon="composebox:fileUpload"></cr-icon>
       ${this.i18n('uploadFile')}
     </button>
@@ -61,7 +61,7 @@
     ${this.showDeepSearch_ ?
     html`<button id="deepSearch" class="dropdown-item"
         @click="${this.onDeepSearchClick_}"
-        ?disabled="${this.inCreateImageMode}">
+        ?disabled="${this.inCreateImageMode_}">
       <cr-icon icon="composebox:deepSearch"></cr-icon>
       ${this.i18n('deepSearch')}
     </button>` : ''}
diff --git a/ui/webui/resources/cr_components/composebox/context_menu_entrypoint.ts b/ui/webui/resources/cr_components/composebox/context_menu_entrypoint.ts
index 9713503..f16ebb73 100644
--- a/ui/webui/resources/cr_components/composebox/context_menu_entrypoint.ts
+++ b/ui/webui/resources/cr_components/composebox/context_menu_entrypoint.ts
@@ -51,7 +51,7 @@
     return {
       inputsDisabled: {type: Boolean},
       showContextMenuDescription: {type: Boolean},
-      inCreateImageMode: {
+      inCreateImageMode_: {
         reflect: true,
         type: Boolean,
       },
@@ -71,7 +71,7 @@
 
   accessor inputsDisabled: boolean = false;
   accessor showContextMenuDescription: boolean = false;
-  protected accessor inCreateImageMode: boolean = false;
+  protected accessor inCreateImageMode_: boolean = false;
   protected accessor tabSuggestions_: TabInfo[] = [];
   protected accessor tabPreviewUrl_: string = '';
   protected accessor tabPreviewsEnabled_: boolean =
@@ -103,7 +103,7 @@
       }});
   }
 
-  protected addTabContext(e: Event) {
+  protected addTabContext_(e: Event) {
     e.stopPropagation();
 
     const tabElement = e.currentTarget! as HTMLButtonElement;
@@ -136,16 +136,16 @@
     this.tabPreviewUrl_ = previewDataUrl || '';
   }
 
-  protected shouldShowTabPreview(): boolean {
+  protected shouldShowTabPreview_(): boolean {
     return this.tabPreviewsEnabled_ && this.tabPreviewUrl_ !== '';
   }
 
-  protected openImageUpload() {
+  protected openImageUpload_() {
     this.fire('open-image-upload');
     this.$.menu.close();
   }
 
-  protected openFileUpload() {
+  protected openFileUpload_() {
     this.fire('open-file-upload');
     this.$.menu.close();
   }
@@ -157,8 +157,8 @@
   }
 
   protected onCreateImageClick_() {
-    this.fire('create-image-click',
-        {inCreateImageMode: this.inCreateImageMode});
+    this.fire(
+        'create-image-click', {inCreateImageMode: this.inCreateImageMode_});
     this.$.menu.close();
   }
 }
diff --git a/ui/webui/resources/cr_components/composebox/contextual_entrypoint_and_carousel.html.ts b/ui/webui/resources/cr_components/composebox/contextual_entrypoint_and_carousel.html.ts
index 08ea7bb..a0ba382 100644
--- a/ui/webui/resources/cr_components/composebox/contextual_entrypoint_and_carousel.html.ts
+++ b/ui/webui/resources/cr_components/composebox/contextual_entrypoint_and_carousel.html.ts
@@ -11,19 +11,10 @@
 export function getHtml(this: ContextualEntrypointAndCarouselElement) {
   // clang-format off
   return html`<!--_html_template_start_-->
-  ${this.showFileCarousel_ ? html`
-    <ntp-composebox-file-carousel
-      id="carousel"
-      .files=${Array.from(this.files_.values())}
-      @delete-file=${this.onDeleteFile_}>
-    </ntp-composebox-file-carousel> ` : ''}
-  ${this.showDropdown && this.showFileCarousel_ ? html`
-  <div class="carousel-divider"></div>` : ''}
-  <!-- Suggestions are slotted in from the parent component. -->
-  <slot id="dropdownMatches"></slot>
-  ${this.contextMenuEnabled_ ? html`
+  ${this.compactMode ? html`
     <div id="contextMenuContainer">
       <composebox-context-menu-entrypoint id="contextEntrypoint"
+          part="composebox-entrypoint"
           class="upload-icon no-overlap"
           @open-image-upload="${this.openImageUpload_}"
           @open-file-upload="${this.openFileUpload_}"
@@ -32,7 +23,7 @@
           @create-image-click="${this.onCreateImageClick_}"
           ?in-create-image-mode="${this.inCreateImageMode_}"
           ?inputs-disabled="${this.inputsDisabled_}"
-          ?show-context-menu-description="${this.showContextMenuDescription_}">
+          ?show-context-menu-description="${false}">
       </composebox-context-menu-entrypoint>
       <composebox-tool-chip
           icon="composebox:deepSearch"
@@ -47,28 +38,68 @@
           @click="${this.onCreateImageClick_}">
       </composebox-tool-chip>
     </div>
-  ` : html`
-    <div id="uploadContainer" class="icon-fade">
-        <cr-icon-button
+      `: ''}
+  ${this.showFileCarousel_ ? html`
+    <ntp-composebox-file-carousel
+      part="composebox-file-carousel"
+      id="carousel"
+      .files=${Array.from(this.files_.values())}
+      @delete-file=${this.onDeleteFile_}>
+    </ntp-composebox-file-carousel> ` : ''}
+  ${this.showDropdown && this.showFileCarousel_ ? html`
+  <div class="carousel-divider"></div>` : ''}
+  <!-- Suggestions are slotted in from the parent component. -->
+  <slot id="dropdownMatches"></slot>
+  ${!this.compactMode ? html`
+    ${this.contextMenuEnabled_ ? html`
+      <div id="contextMenuContainer">
+        <composebox-context-menu-entrypoint id="contextEntrypoint"
             class="upload-icon no-overlap"
-            id="imageUploadButton"
-            iron-icon="composebox:imageUpload"
-            title="${this.i18n('composeboxImageUploadButtonTitle')}"
-            .disabled="${this.inputsDisabled_}"
-            @click="${this.openImageUpload_}">
-        </cr-icon-button>
-        ${this.composeboxShowPdfUpload_ ? html`
-        <cr-icon-button
-            class="upload-icon no-overlap"
-            id="fileUploadButton"
-            iron-icon="composebox:fileUpload"
-            title="${this.i18n('composeboxPdfUploadButtonTitle')}"
-            .disabled="${this.inputsDisabled_}"
-            @click="${this.openFileUpload_}">
-        </cr-icon-button>
-        `: ''}
-    </div>
-  `}
+            @open-image-upload="${this.openImageUpload_}"
+            @open-file-upload="${this.openFileUpload_}"
+            @add-tab-context="${this.addTabContext_}"
+            @deep-search-click="${this.onDeepSearchClick_}"
+            @create-image-click="${this.onCreateImageClick_}"
+            ?in-create-image-mode="${this.inCreateImageMode_}"
+            ?inputs-disabled="${this.inputsDisabled_}"
+            ?show-context-menu-description="${this.showContextMenuDescription_}">
+        </composebox-context-menu-entrypoint>
+        <composebox-tool-chip
+            icon="composebox:deepSearch"
+            label="${this.i18n('deepSearch')}"
+            ?visible="${this.inDeepSearchMode_}"
+            @click="${this.onDeepSearchClick_}">
+        </composebox-tool-chip>
+        <composebox-tool-chip
+            icon="composebox:nanoBanana"
+            label="${this.i18n('createImages')}"
+            ?visible="${this.inCreateImageMode_}"
+            @click="${this.onCreateImageClick_}">
+        </composebox-tool-chip>
+      </div>
+    ` : html`
+      <div id="uploadContainer" class="icon-fade">
+          <cr-icon-button
+              class="upload-icon no-overlap"
+              id="imageUploadButton"
+              iron-icon="composebox:imageUpload"
+              title="${this.i18n('composeboxImageUploadButtonTitle')}"
+              .disabled="${this.inputsDisabled_}"
+              @click="${this.openImageUpload_}">
+          </cr-icon-button>
+          ${this.composeboxShowPdfUpload_ ? html`
+          <cr-icon-button
+              class="upload-icon no-overlap"
+              id="fileUploadButton"
+              iron-icon="composebox:fileUpload"
+              title="${this.i18n('composeboxPdfUploadButtonTitle')}"
+              .disabled="${this.inputsDisabled_}"
+              @click="${this.openFileUpload_}">
+          </cr-icon-button>
+          `: ''}
+      </div>
+    `}
+  `: ''}
   <input type="file"
       accept="${this.imageFileTypes_}"
       id="imageInput"
diff --git a/ui/webui/resources/cr_components/composebox/contextual_entrypoint_and_carousel.ts b/ui/webui/resources/cr_components/composebox/contextual_entrypoint_and_carousel.ts
index 429edb9b..21d6f66 100644
--- a/ui/webui/resources/cr_components/composebox/contextual_entrypoint_and_carousel.ts
+++ b/ui/webui/resources/cr_components/composebox/contextual_entrypoint_and_carousel.ts
@@ -69,6 +69,7 @@
   static override get properties() {
     return {
       showDropdown: {type: Boolean},
+      compactMode: {type: Boolean},
       attachmentFileTypes_: {type: String},
       contextMenuEnabled_: {type: Boolean},
       files_: {type: Object},
@@ -98,6 +99,7 @@
   }
 
   accessor showDropdown: boolean = false;
+  accessor compactMode: boolean = false;
   protected accessor attachmentFileTypes_: string =
       loadTimeData.getString('composeboxAttachmentFileTypes');
   protected accessor contextMenuEnabled_: boolean =
diff --git a/ui/webui/resources/cr_components/searchbox/icons/enterprise.svg b/ui/webui/resources/cr_components/searchbox/icons/enterprise.svg
new file mode 100644
index 0000000..178d719
--- /dev/null
+++ b/ui/webui/resources/cr_components/searchbox/icons/enterprise.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px" fill="#e3e3e3"><path d="M96-144v-672h384v144h384v528H96Zm72-72h72v-72h-72v72Zm0-152h72v-72h-72v72Zm0-152h72v-72h-72v72Zm0-152h72v-72h-72v72Zm168 456h72v-72h-72v72Zm0-152h72v-72h-72v72Zm0-152h72v-72h-72v72Zm0-152h72v-72h-72v72Zm144 456h312v-384H480v80h72v72h-72v80h72v72h-72v80Zm168-232v-72h72v72h-72Zm0 152v-72h72v72h-72Z"/></svg>
\ No newline at end of file
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox.css b/ui/webui/resources/cr_components/searchbox/searchbox.css
index 28a6bc709..f07f349 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox.css
+++ b/ui/webui/resources/cr_components/searchbox/searchbox.css
@@ -362,18 +362,20 @@
   inset-inline-start: 16px;
 }
 
-:host([realbox-layout-mode='Tall']) {
+:host([ntp-realbox-next-enabled]) input::placeholder {
+  font-size: 18px;
+  font-style: normal;
+  font-weight: 500;
+}
+
+:host([ntp-realbox-next-enabled]) {
   --cr-searchbox-border-radius: 26px;
-  --cr-searchbox-compose-button-position-top: 12px;
   --cr-searchbox-dropdown-padding-bottom: 12px;
-  --cr-searchbox-dropdown-padding-top: 62px;
-  --cr-searchbox-height: 112px;
-  --cr-searchbox-icon-height: 40px;
-  --cr-searchbox-input-padding-top_: 22px;
+  --cr-searchbox-icon-size: 40px;
   --text-input-inline-start-spacing: 16px;
 }
 
-:host([realbox-layout-mode='Tall']) cr-searchbox-dropdown::part(dropdown-content) {
+:host([ntp-realbox-next-enabled]) cr-searchbox-dropdown::part(dropdown-content) {
   background-color: unset;
   border-radius: unset;
   box-shadow: unset;
@@ -384,7 +386,15 @@
   padding-top: unset;
 }
 
-:host([realbox-layout-mode='Tall']) .dropdownContainer {
+:host([ntp-realbox-next-enabled]) contextual-entrypoint-and-carousel::part(composebox-entrypoint) {
+  z-index: 100;
+}
+
+:host([ntp-realbox-next-enabled]) contextual-entrypoint-and-carousel::part(composebox-file-carousel) {
+  margin-top: 16px;
+}
+
+:host([ntp-realbox-next-enabled]) .dropdownContainer {
   background-color: var(--color-searchbox-results-background);
   border-radius: var(--cr-searchbox-border-radius);
   box-shadow: var(--cr-searchbox-shadow);
@@ -395,6 +405,17 @@
   padding-top: var(--cr-searchbox-dropdown-padding-top);
 }
 
+:host([realbox-layout-mode='Tall']) {
+  --cr-searchbox-compose-button-position-top: 12px;
+  --cr-searchbox-dropdown-padding-top: 60px;
+  --cr-searchbox-height: 108px;
+  --cr-searchbox-input-padding-top_: 22px;
+}
+
+:host([realbox-layout-mode='Tall'][has-context-files_]) {
+  --cr-searchbox-dropdown-padding-top: 48px;
+}
+
 @media (forced-colors: active) {
   :host([realbox-layout-mode='Tall']) .dropdownContainer {
     border: 1px solid ActiveBorder;
@@ -415,12 +436,6 @@
   padding-inline-start: 24px;
 }
 
-:host([realbox-layout-mode='Tall']) input::placeholder {
-  font-size: 18px;
-  font-style: normal;
-  font-weight: 500;
-}
-
 :host([realbox-layout-mode='Tall']:not([dropdown-is-visible])) cr-searchbox-icon,
 :host([realbox-layout-mode='Tall'][dropdown-is-visible]) .searchbox-icon-button-container {
   display: none;
@@ -428,7 +443,7 @@
 
 :host([realbox-layout-mode='Tall']) .searchbox-icon-button {
   bottom: 12px;
-  height: var(--cr-searchbox-icon-height);
+  height: var(--cr-searchbox-icon-size);
   padding: 8px 12px;
 }
 
@@ -449,13 +464,43 @@
 
 :host([realbox-layout-mode='Tall'][searchbox-lens-search-enabled_]) #voiceSearchButton,
 :host([realbox-layout-mode='Tall'][searchbox-lens-search-enabled_]):host-context([dir='rtl']) #voiceSearchButton {
-  inset-inline-end: calc(12px + var(--cr-searchbox-icon-height) + var(--cr-searchbox-icon-spacing));
+  inset-inline-end: calc(12px + var(--cr-searchbox-icon-size) + var(--cr-searchbox-icon-spacing));
 }
 
 :host-context([realbox-layout-mode='Tall']) #composeButton {
   --cr-button-height: 36px;
 }
 
+:host([realbox-layout-mode='Compact']) {
+  --cr-searchbox-dropdown-padding-top: 12px;
+  --cr-searchbox-height: 56px;
+}
+
+:host([realbox-layout-mode='Compact']) #inputWrapper {
+  z-index: 99;
+}
+
+:host([realbox-layout-mode='Compact']) cr-searchbox-icon {
+  display: none;
+}
+
+:host([realbox-layout-mode='Compact'][compose-button-enabled][searchbox-lens-search-enabled_]) #lensSearchButton {
+  inset-inline-end: calc(var(--cr-searchbox-icon-spacing) + var(--cr-compose-button-width));
+}
+
+:host([realbox-layout-mode='Compact'][compose-button-enabled][searchbox-lens-search-enabled_]) #voiceSearchButton,
+:host([realbox-layout-mode='Compact'][compose-button-enabled][searchbox-lens-search-enabled_]):host-context([dir='rtl']) #voiceSearchButton {
+  inset-inline-end: calc(var(--cr-searchbox-icon-size) + var(--cr-searchbox-icon-spacing) + var(--cr-compose-button-width));
+}
+
+:host([realbox-layout-mode='Compact']) .dropdownContainer {
+  z-index: auto;
+}
+
+:host([realbox-layout-mode='Compact']) cr-searchbox-dropdown::part(dropdown-content) {
+  padding-top: 12px;
+}
+
 :host-context([realbox-layout-mode='Tall']) #errorScrim {
   border-radius: inherit;
 }
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox.html.ts b/ui/webui/resources/cr_components/searchbox/searchbox.html.ts
index 15c72f7..ea33a8e1 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox.html.ts
+++ b/ui/webui/resources/cr_components/searchbox/searchbox.html.ts
@@ -78,16 +78,19 @@
     ` : ''}
   ` : ''}
 
-  ${this.realboxLayoutMode === 'Tall' ? html`
+  ${this.ntpRealboxNextEnabled ? html`
     <div class="dropdownContainer">
       <contextual-entrypoint-and-carousel id="context"
+          part="contextual-entrypoint-and-carousel"
+          exportparts="composebox-entrypoint"
           @add-tab-context="${this.addTabContext_}"
           @add-file-context="${this.addFileContext_}"
           @delete-context="${this.deleteContext_}"
           @refresh-tab-suggestions="${this.refreshTabSuggestions_}"
           @on-context-files-changed="${this.onContextFilesChanged_}"
           @on-file-validation-error="${this.onFileValidationError_}"
-          ?show-dropdown="${this.dropdownIsVisible}">
+          ?show-dropdown="${this.dropdownIsVisible}"
+          ?compact-mode="${this.realboxLayoutMode === 'Compact'}">
         <cr-searchbox-dropdown id="matches" part="searchbox-dropdown"
             exportparts="dropdown-content"
             role="listbox" .result="${this.result_}"
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox.ts b/ui/webui/resources/cr_components/searchbox/searchbox.ts
index 4ab5137..732b7e42 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox.ts
+++ b/ui/webui/resources/cr_components/searchbox/searchbox.ts
@@ -275,6 +275,11 @@
         reflect: true,
       },
 
+      ntpRealboxNextEnabled: {
+        type: Boolean,
+        reflect: true,
+      },
+
       composeboxEnabled: {type: Boolean},
 
       composeButtonEnabled: {type: Boolean},
@@ -393,6 +398,8 @@
       loadTimeData.getBoolean('searchboxCr23SteadyStateShadow');
   accessor realboxLayoutMode: string =
       loadTimeData.getString('realboxLayoutMode');
+  accessor ntpRealboxNextEnabled: boolean =
+      loadTimeData.getBoolean('ntpRealboxNextEnabled');
   accessor composeboxEnabled: boolean = false;
   accessor composeButtonEnabled: boolean = false;
   accessor showThumbnail: boolean = false;
@@ -464,7 +471,7 @@
       this.placeholderCycler_.start();
     }
 
-    if (this.realboxLayoutMode === 'Tall') {
+    if (this.ntpRealboxNextEnabled) {
       this.pageHandler_.notifySessionStarted();
     }
   }
@@ -472,7 +479,7 @@
   override disconnectedCallback() {
     super.disconnectedCallback();
 
-    if (this.realboxLayoutMode === 'Tall') {
+    if (this.ntpRealboxNextEnabled) {
       this.pageHandler_.notifySessionAbandoned();
     }
 
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox_action.css b/ui/webui/resources/cr_components/searchbox/searchbox_action.css
index d7fcdbc4..5d325f3 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox_action.css
+++ b/ui/webui/resources/cr_components/searchbox/searchbox_action.css
@@ -11,7 +11,8 @@
 
 :host {
   --action-height: 28px;
-  border: solid 1px var(--color-searchbox-results-action-chip);
+  --border-width: 1px;
+  border: solid var(--border-width) var(--color-searchbox-results-action-chip);
   border-radius: 8px;
   display: flex;
   height: var(--action-height);
@@ -19,6 +20,7 @@
   outline: none;
   padding-inline-end: 8px;
   padding-inline-start: 8px;
+  position: relative;
 }
 
 :host(:hover) {
@@ -27,10 +29,35 @@
 
 :host(:focus) {
   margin: 2px;
-  border: solid 1px var(--color-searchbox-results-action-chip);
+  border: solid var(--border-width) var(--color-searchbox-results-action-chip);
   box-shadow: none;
 }
 
+:host(.selected:hover) {
+  background-color: var(--color-searchbox-results-button-selected-hover);
+}
+
+:host(:active) #overlay {
+  background-color:
+      var(--color-omnibox-results-button-ink-drop-selected-row-hovered);
+}
+
+:host(.selected:active) #overlay {
+  background-color:
+      var(--color-omnibox-results-button-ink-drop-selected-row-selected);
+}
+
+#overlay {
+  --overlay-inset: calc(var(--border-width) * -1);
+  border-radius: inherit;
+  display: inherit;
+  position: absolute;
+  top: var(--overlay-inset);
+  left: var(--overlay-inset);
+  right: var(--overlay-inset);
+  bottom: var(--overlay-inset);
+}
+
 .contents {
   align-items: center;
   display: flex;
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox_action.html.ts b/ui/webui/resources/cr_components/searchbox/searchbox_action.html.ts
index 4e29afb..99c1e1e 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox_action.html.ts
+++ b/ui/webui/resources/cr_components/searchbox/searchbox_action.html.ts
@@ -9,6 +9,7 @@
 export function getHtml(this: SearchboxActionElement) {
   // clang-format off
   return html`<!--_html_template_start_-->
+<div id="overlay"></div>
 <div class="contents" title="${this.suggestionContents}">
   <div id="action-icon" style="${this.iconStyle_}"></div>
   <div id="text" .innerHTML="${this.hintHtml_}"></div>
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox_compose_button.css b/ui/webui/resources/cr_components/searchbox/searchbox_compose_button.css
index 14e7f55..097781b3 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox_compose_button.css
+++ b/ui/webui/resources/cr_components/searchbox/searchbox_compose_button.css
@@ -158,13 +158,13 @@
   right: unset;
 }
 
-:host-context([realbox-layout-mode='Tall']) #composeButton {
+:host-context([ntp-realbox-next-enabled]) #composeButton {
   font-size: 13px;
   font-weight: 500;
 }
 
 :host-context([realbox-layout-mode='Tall']) .glow-container {
-  height: var(--cr-searchbox-icon-height);
+  height: var(--cr-searchbox-icon-size);
   top: var(--cr-searchbox-compose-button-position-top);
   transform: none;
 }
diff --git a/url/README.md b/url/README.md
index 0764f12..a56e0f5 100644
--- a/url/README.md
+++ b/url/README.md
@@ -74,6 +74,19 @@
 still useful. But some STL strings and calls to base functions have gradually
 been added in places where doing so is possible.
 
+## Unsafe buffer usages
+
+To ensure that the valid length of a buffer is always reliably conveyed, we are
+in the process of migrating functions that take a raw pointer and a size for
+string data. These functions are being updated to accept `std::string_view` or
+`std::u16string_view` instead. This change also applies to functions that only
+accept a raw pointer, which are being updated to take a `string_view` to
+prevent buffer overflows.
+
+Currently, the codebase contains a mix of both the old, unsafe functions and
+the new, safer `string_view`-based functions. Our goal is to eventually
+convert all of them. This ongoing effort is tracked in crbug.com/350788890.
+
 ## Caution for terminologies
 
 Due to historical usage, the term "Standard URL" is currently used within the
diff --git a/url/gurl.cc b/url/gurl.cc
index 40a171d5..ee910fb 100644
--- a/url/gurl.cc
+++ b/url/gurl.cc
@@ -77,9 +77,8 @@
 template <typename T, typename CharT>
 void GURL::InitCanonical(T input_spec, bool trim_path_end) {
   url::StdStringCanonOutput output(&spec_);
-  is_valid_ = url::Canonicalize(
-      input_spec.data(), static_cast<int>(input_spec.length()), trim_path_end,
-      NULL, &output, &parsed_);
+  is_valid_ =
+      url::Canonicalize(input_spec, trim_path_end, nullptr, &output, &parsed_);
 
   output.Complete();  // Must be done before using string.
   if (is_valid_ && SchemeIsFileSystem()) {
diff --git a/url/gurl.h b/url/gurl.h
index e241d2f..4dabeb71 100644
--- a/url/gurl.h
+++ b/url/gurl.h
@@ -308,24 +308,18 @@
   std::string_view scheme() const LIFETIME_BOUND {
     return ComponentStringPiece(parsed_.scheme);
   }
-  // Deprecated. Use scheme(). See crbug.com/448174617.
-  std::string_view scheme_piece() const LIFETIME_BOUND { return scheme(); }
 
   bool has_username() const { return parsed_.username.is_valid(); }
   std::string GetUsername() const { return ComponentString(parsed_.username); }
   std::string_view username() const LIFETIME_BOUND {
     return ComponentStringPiece(parsed_.username);
   }
-  // Deprecated. Use username(). See crbug.com/448174617.
-  std::string_view username_piece() const LIFETIME_BOUND { return username(); }
 
   bool has_password() const { return parsed_.password.is_valid(); }
   std::string GetPassword() const { return ComponentString(parsed_.password); }
   std::string_view password() const LIFETIME_BOUND {
     return ComponentStringPiece(parsed_.password);
   }
-  // Deprecated. Use password(). See crbug.com/448174617.
-  std::string_view password_piece() const LIFETIME_BOUND { return password(); }
 
   // The host may be a hostname, an IPv4 address, or an IPv6 literal surrounded
   // by square brackets, like "[2001:db8::1]". To exclude these brackets, use
@@ -338,8 +332,6 @@
   std::string_view host() const LIFETIME_BOUND {
     return ComponentStringPiece(parsed_.host);
   }
-  // Deprecated. Use host(). See crbug.com/448174617.
-  std::string_view host_piece() const LIFETIME_BOUND { return host(); }
 
   // The port if one is explicitly specified. Most callers will want IntPort()
   // or EffectiveIntPort() instead of these. The getters will not include the
@@ -349,8 +341,6 @@
   std::string_view port() const LIFETIME_BOUND {
     return ComponentStringPiece(parsed_.port);
   }
-  // Deprecated. Use port(). See crbug.com/448174617.
-  std::string_view port_piece() const LIFETIME_BOUND { return port(); }
 
   // Including first slash following host, up to the query. The URL
   // "http://www.google.com/" has a path of "/".
@@ -359,8 +349,6 @@
   std::string_view path() const LIFETIME_BOUND {
     return ComponentStringPiece(parsed_.path);
   }
-  // Deprecated. Use path(). See crbug.com/448174617.
-  std::string_view path_piece() const LIFETIME_BOUND { return path(); }
 
   // Stuff following '?' up to the ref. The getters will not include the '?'.
   bool has_query() const { return parsed_.query.is_valid(); }
@@ -368,8 +356,6 @@
   std::string_view query() const LIFETIME_BOUND {
     return ComponentStringPiece(parsed_.query);
   }
-  // Deprecated. Use query(). See crbug.com/448174617.
-  std::string_view query_piece() const LIFETIME_BOUND { return query(); }
 
   // Stuff following '#' to the end of the string. This will be %-escaped UTF-8.
   // The getters will not include the '#'.
@@ -378,8 +364,6 @@
   std::string_view ref() const LIFETIME_BOUND {
     return ComponentStringPiece(parsed_.ref);
   }
-  // Deprecated. Use ref(). See crbug.com/448174617.
-  std::string_view ref_piece() const LIFETIME_BOUND { return ref(); }
 
   // Returns a parsed version of the port. Can also be any of the special
   // values defined in Parsed for ExtractPort.
diff --git a/url/url_canon.h b/url/url_canon.h
index 01d53c81..8ee0916 100644
--- a/url/url_canon.h
+++ b/url/url_canon.h
@@ -276,35 +276,31 @@
 
 // Searches for whitespace that should be removed from the middle of URLs, and
 // removes it. Removed whitespace are tabs and newlines, but NOT spaces. Spaces
-// are preserved, which is what most browsers do. A pointer to the output will
-// be returned, and the length of that output will be in |output_len|.
+// are preserved, which is what most browsers do. A string_view pointing to the
+// output will be returned.
 //
 // This should be called before parsing if whitespace removal is desired (which
 // it normally is when you are canonicalizing).
 //
 // If no whitespace is removed, this function will not use the buffer and will
-// return a pointer to the input, to avoid the extra copy. If modification is
-// required, the given |buffer| will be used and the returned pointer will
+// return the input string_view, to avoid the extra copy. If modification is
+// required, the given |buffer| will be used and the returned string_view will
 // point to the beginning of the buffer.
 //
 // Therefore, callers should not use the buffer, since it may actually be empty,
-// use the computed pointer and |*output_len| instead.
+// use the returned string_view instead.
 //
 // If |input| contained both removable whitespace and a raw `<` character,
 // |potentially_dangling_markup| will be set to `true`. Otherwise, it will be
 // left untouched.
 COMPONENT_EXPORT(URL)
-const char* RemoveURLWhitespace(const char* input,
-                                int input_len,
-                                CanonOutputT<char>* buffer,
-                                int* output_len,
-                                bool* potentially_dangling_markup);
+std::string_view RemoveUrlWhitespace(std::string_view input,
+                                     CanonOutputT<char>* buffer,
+                                     bool* potentially_dangling_markup);
 COMPONENT_EXPORT(URL)
-const char16_t* RemoveURLWhitespace(const char16_t* input,
-                                    int input_len,
-                                    CanonOutputT<char16_t>* buffer,
-                                    int* output_len,
-                                    bool* potentially_dangling_markup);
+std::u16string_view RemoveUrlWhitespace(std::u16string_view input,
+                                        CanonOutputT<char16_t>* buffer,
+                                        bool* potentially_dangling_markup);
 
 // IDN ------------------------------------------------------------------------
 
diff --git a/url/url_canon_etc.cc b/url/url_canon_etc.cc
index 953d86a..71bdb147 100644
--- a/url/url_canon_etc.cc
+++ b/url/url_canon_etc.cc
@@ -27,26 +27,27 @@
 // It sucks that we have to do this, since this takes about 13% of the total URL
 // canonicalization time.
 template <typename CHAR>
-const CHAR* DoRemoveURLWhitespace(const CHAR* input,
-                                  int input_len,
-                                  CanonOutputT<CHAR>* buffer,
-                                  int* output_len,
-                                  bool* potentially_dangling_markup) {
+std::basic_string_view<CHAR> DoRemoveUrlWhitespace(
+    std::basic_string_view<CHAR> input,
+    CanonOutputT<CHAR>* buffer,
+    bool* potentially_dangling_markup) {
   // Fast verification that there's nothing that needs removal. This is the 99%
   // case, so we want it to be fast and don't care about impacting the speed
   // when we do find whitespace.
   bool found_whitespace = false;
-  if (sizeof(*input) == 1 && input_len >= kMinimumLengthForSIMD) {
+  if (sizeof(CHAR) == 1 && input.length() >= kMinimumLengthForSIMD) {
     // For large strings, memchr is much faster than any scalar code we can
     // write, even if we need to run it three times. (If this turns out to still
     // be a bottleneck, we could write our own vector code, but given that
     // memchr is so fast, it's unlikely to be relevant.)
-    found_whitespace = UNSAFE_TODO(memchr(input, '\n', input_len)) != nullptr ||
-                       UNSAFE_TODO(memchr(input, '\r', input_len)) != nullptr ||
-                       UNSAFE_TODO(memchr(input, '\t', input_len)) != nullptr;
+    const CHAR* data = input.data();
+    size_t input_len = input.length();
+    found_whitespace = UNSAFE_TODO(memchr(data, '\n', input_len)) != nullptr ||
+                       UNSAFE_TODO(memchr(data, '\r', input_len)) != nullptr ||
+                       UNSAFE_TODO(memchr(data, '\t', input_len)) != nullptr;
   } else {
-    for (int i = 0; i < input_len; i++) {
-      if (!IsRemovableURLWhitespace(UNSAFE_TODO(input[i]))) {
+    for (const CHAR ch : input) {
+      if (!IsRemovableURLWhitespace(ch)) {
         continue;
       }
       found_whitespace = true;
@@ -57,7 +58,6 @@
   if (!found_whitespace) {
     // Didn't find any whitespace, we don't need to do anything. We can just
     // return the input as the output.
-    *output_len = input_len;
     return input;
   }
 
@@ -66,24 +66,21 @@
   // TODO(mkwst): Ideally, this would use something like `base::StartsWith`, but
   // that turns out to be difficult to do correctly given this function's
   // character type templating.
-  if (input_len > 5 && input[0] == 'd' && UNSAFE_TODO(input[1]) == 'a' &&
-      UNSAFE_TODO(input[2]) == 't' && UNSAFE_TODO(input[3]) == 'a' &&
-      UNSAFE_TODO(input[4]) == ':') {
-    *output_len = input_len;
+  if (input.length() > 5 && input[0] == 'd' && input[1] == 'a' &&
+      input[2] == 't' && input[3] == 'a' && input[4] == ':') {
     return input;
   }
 
   // Remove the whitespace into the new buffer and return it.
-  for (int i = 0; i < input_len; i++) {
-    if (!IsRemovableURLWhitespace(UNSAFE_TODO(input[i]))) {
-      if (potentially_dangling_markup && UNSAFE_TODO(input[i]) == 0x3C) {
+  for (const CHAR ch : input) {
+    if (!IsRemovableURLWhitespace(ch)) {
+      if (potentially_dangling_markup && ch == 0x3C) {
         *potentially_dangling_markup = true;
       }
-      buffer->push_back(UNSAFE_TODO(input[i]));
+      buffer->push_back(ch);
     }
   }
-  *output_len = buffer->length();
-  return buffer->data();
+  return buffer->view();
 }
 
 // Contains the canonical version of each possible input letter in the scheme
@@ -332,22 +329,16 @@
 
 }  // namespace
 
-const char* RemoveURLWhitespace(const char* input,
-                                int input_len,
-                                CanonOutputT<char>* buffer,
-                                int* output_len,
-                                bool* potentially_dangling_markup) {
-  return DoRemoveURLWhitespace(input, input_len, buffer, output_len,
-                               potentially_dangling_markup);
+std::string_view RemoveUrlWhitespace(std::string_view input,
+                                     CanonOutputT<char>* buffer,
+                                     bool* potentially_dangling_markup) {
+  return DoRemoveUrlWhitespace(input, buffer, potentially_dangling_markup);
 }
 
-const char16_t* RemoveURLWhitespace(const char16_t* input,
-                                    int input_len,
-                                    CanonOutputT<char16_t>* buffer,
-                                    int* output_len,
-                                    bool* potentially_dangling_markup) {
-  return DoRemoveURLWhitespace(input, input_len, buffer, output_len,
-                               potentially_dangling_markup);
+std::u16string_view RemoveUrlWhitespace(std::u16string_view input,
+                                        CanonOutputT<char16_t>* buffer,
+                                        bool* potentially_dangling_markup) {
+  return DoRemoveUrlWhitespace(input, buffer, potentially_dangling_markup);
 }
 
 char CanonicalSchemeChar(char16_t ch) {
diff --git a/url/url_canon_unittest.cc b/url/url_canon_unittest.cc
index 3927dc73..6864033 100644
--- a/url/url_canon_unittest.cc
+++ b/url/url_canon_unittest.cc
@@ -493,9 +493,9 @@
        L"a\x200c"
        L"b\x200d"
        L"c",
-       "xn--abc-9m0ag",
-       Component(0, 13),
-       CanonHostInfo::NEUTRAL, -1, ""},
+       "a%E2%80%8Cb%E2%80%8Dc",
+       Component(0, 21),
+       CanonHostInfo::BROKEN, -1, ""},
 
       // ZWJ between Devanagari characters was still mapped away in UTS 46
       // transitional handling. IDNA 2008 gives xn--11bo0mv54g.
diff --git a/url/url_features.cc b/url/url_features.cc
index 55cdc8b..230a6060 100644
--- a/url/url_features.cc
+++ b/url/url_features.cc
@@ -20,7 +20,7 @@
   return base::FeatureList::IsEnabled(kDisallowSpaceCharacterInURLHostParsing);
 }
 
-BASE_FEATURE(kUseIDNAContextJRules, base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kUseIDNAContextJRules, base::FEATURE_ENABLED_BY_DEFAULT);
 
 bool IsUsingIDNAContextJRules() {
   // If the FeatureList isn't available yet, fall back to the feature's default
diff --git a/url/url_parse_internal.h b/url/url_parse_internal.h
index f995575c..0622d7d 100644
--- a/url/url_parse_internal.h
+++ b/url/url_parse_internal.h
@@ -59,6 +59,26 @@
   }
 }
 
+// This shrinks the input URL string to eliminate "should-be-trimmed"
+// characters.
+template <typename CHAR>
+inline std::basic_string_view<CHAR> TrimUrl(std::basic_string_view<CHAR> spec,
+                                            bool trim_path_end = true) {
+  // Strip leading whitespace and control characters.
+  while (!spec.empty() && ShouldTrimFromURL(spec[0])) {
+    spec = spec.substr(1);
+  }
+
+  if (trim_path_end) {
+    // Strip trailing whitespace and control characters. We need the empty()
+    // test for when the input string is all blanks.
+    while (!spec.empty() && ShouldTrimFromURL(spec[spec.length() - 1])) {
+      spec = spec.substr(0, spec.length() - 1);
+    }
+  }
+  return spec;
+}
+
 // Counts the number of consecutive slashes or backslashes starting at the given
 // offset in the given string of the given length. A slash and backslash can be
 // mixed.
diff --git a/url/url_util.cc b/url/url_util.cc
index ad9d781..444caa4 100644
--- a/url/url_util.cc
+++ b/url/url_util.cc
@@ -234,12 +234,11 @@
   // Before extracting scheme, canonicalize the URL to remove any whitespace.
   // This matches the canonicalization done in DoCanonicalize function.
   STACK_UNINITIALIZED RawCanonOutputT<CHAR> whitespace_buffer;
-  int spec_len;
-  const CHAR* spec =
-      RemoveURLWhitespace(str, str_len, &whitespace_buffer, &spec_len, nullptr);
+  std::basic_string_view<CHAR> spec = RemoveUrlWhitespace(
+      {str, base::checked_cast<size_t>(str_len)}, &whitespace_buffer, nullptr);
 
   Component our_scheme;
-  if (!ExtractScheme(spec, spec_len, &our_scheme)) {
+  if (!ExtractScheme(spec, &our_scheme)) {
     // No scheme.
     if (found_scheme)
       *found_scheme = Component();
@@ -247,31 +246,26 @@
   }
   if (found_scheme)
     *found_scheme = our_scheme;
-  return DoCompareSchemeComponent(spec, our_scheme, compare);
+  return DoCompareSchemeComponent(spec.data(), our_scheme, compare);
 }
 
 template <typename CHAR>
-bool DoCanonicalize(const CHAR* spec,
-                    int spec_len,
+bool DoCanonicalize(std::basic_string_view<CHAR> spec,
                     bool trim_path_end,
                     WhitespaceRemovalPolicy whitespace_policy,
                     CharsetConverter* charset_converter,
                     CanonOutput* output,
                     Parsed* output_parsed) {
   // Trim leading C0 control characters and spaces.
-  int begin = 0;
-  TrimURL(spec, &begin, &spec_len, trim_path_end);
-  DCHECK(0 <= begin && begin <= spec_len);
-  spec += begin;
-  spec_len -= begin;
+  spec = TrimUrl(spec, trim_path_end);
 
-  output->ReserveSizeIfNeeded(spec_len);
+  output->ReserveSizeIfNeeded(spec.length());
 
   // Remove any whitespace from the middle of the relative URL if necessary.
   // Possibly this will result in copying to the new buffer.
   STACK_UNINITIALIZED RawCanonOutputT<CHAR> whitespace_buffer;
   if (whitespace_policy == REMOVE_WHITESPACE) {
-    spec = RemoveURLWhitespace(spec, spec_len, &whitespace_buffer, &spec_len,
+    spec = RemoveUrlWhitespace(spec, &whitespace_buffer,
                                &output_parsed->potentially_dangling_markup);
   }
 
@@ -286,53 +280,52 @@
   // has no meaning as an absolute path name. This is because browsers on Mac
   // & Unix don't generally do this, so there is no compatibility reason for
   // doing so.
-  if (DoesBeginUNCPath(spec, 0, spec_len, false) ||
-      DoesBeginWindowsDriveSpec(spec, 0, spec_len)) {
-    return CanonicalizeFileURL(
-        spec, spec_len, ParseFileURL(std::basic_string_view(spec, spec_len)),
-        charset_converter, output, output_parsed);
+  if (DoesBeginUNCPath(spec.data(), 0, spec.length(), false) ||
+      DoesBeginWindowsDriveSpec(spec.data(), 0, spec.length())) {
+    return CanonicalizeFileURL(spec.data(), spec.length(), ParseFileURL(spec),
+                               charset_converter, output, output_parsed);
   }
 #endif
 
   Component scheme;
-  if (!ExtractScheme(spec, spec_len, &scheme))
+  if (!ExtractScheme(spec, &scheme)) {
     return false;
+  }
 
   // This is the parsed version of the input URL, we have to canonicalize it
   // before storing it in our object.
   bool success;
   SchemeType scheme_type = SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION;
-  if (DoCompareSchemeComponent(spec, scheme, url::kFileScheme)) {
+  if (DoCompareSchemeComponent(spec.data(), scheme, url::kFileScheme)) {
     // File URLs are special.
-    success = CanonicalizeFileURL(
-        spec, spec_len, ParseFileURL(std::basic_string_view(spec, spec_len)),
-        charset_converter, output, output_parsed);
-  } else if (DoCompareSchemeComponent(spec, scheme, url::kFileSystemScheme)) {
+    success =
+        CanonicalizeFileURL(spec.data(), spec.length(), ParseFileURL(spec),
+                            charset_converter, output, output_parsed);
+  } else if (DoCompareSchemeComponent(spec.data(), scheme,
+                                      url::kFileSystemScheme)) {
     // Filesystem URLs are special.
-    success = CanonicalizeFileSystemURL(
-        spec, ParseFileSystemURL(std::basic_string_view(spec, spec_len)),
-        charset_converter, output, output_parsed);
+    success =
+        CanonicalizeFileSystemURL(spec.data(), ParseFileSystemURL(spec),
+                                  charset_converter, output, output_parsed);
 
-  } else if (DoIsStandard(std::optional(scheme.as_string_view_on(spec)),
+  } else if (DoIsStandard(std::optional(scheme.as_string_view_on(spec.data())),
                           &scheme_type)) {
     // All "normal" URLs.
-    success = CanonicalizeStandardURL(
-        spec, ParseStandardURL(std::basic_string_view(spec, spec_len)),
-        scheme_type, charset_converter, output, output_parsed);
+    success = CanonicalizeStandardURL(spec.data(), ParseStandardURL(spec),
+                                      scheme_type, charset_converter, output,
+                                      output_parsed);
 
   } else {
     // Non-special scheme URLs like data:, mailto: and javascript:.
-    if (!DoIsOpaqueNonSpecial(spec, scheme)) {
+    if (!DoIsOpaqueNonSpecial(spec.data(), scheme)) {
       success = CanonicalizeNonSpecialURL(
-          spec, spec_len,
-          ParseNonSpecialURLInternal(std::basic_string_view(spec, spec_len),
-                                     trim_path_end),
-          charset_converter, *output, *output_parsed);
+          spec.data(), spec.length(),
+          ParseNonSpecialURLInternal(spec, trim_path_end), charset_converter,
+          *output, *output_parsed);
     } else {
-      success = CanonicalizePathURL(
-          spec, spec_len,
-          ParsePathURL(std::basic_string_view(spec, spec_len), trim_path_end),
-          output, output_parsed);
+      success = CanonicalizePathURL(spec.data(), spec.length(),
+                                    ParsePathURL(spec, trim_path_end), output,
+                                    output_parsed);
     }
   }
   return success;
@@ -348,13 +341,9 @@
   // Remove any whitespace from the middle of the relative URL, possibly
   // copying to the new buffer.
   STACK_UNINITIALIZED RawCanonOutputT<CHAR> whitespace_buffer;
-  int relative_length;
-  // TODO(crbug.com/350788890): RemoveURLWhitespace() should accept
-  // string_views.
-  const CHAR* relative = RemoveURLWhitespace(
-      in_relative.data(), base::checked_cast<int>(in_relative.length()),
-      &whitespace_buffer, &relative_length,
-      &output_parsed->potentially_dangling_markup);
+  std::basic_string_view<CHAR> relative =
+      RemoveUrlWhitespace(in_relative, &whitespace_buffer,
+                          &output_parsed->potentially_dangling_markup);
 
   bool base_is_authority_based = false;
   bool base_is_hierarchical = false;
@@ -372,7 +361,8 @@
   bool is_relative;
   Component relative_component;
   // TODO(crbug.com/350788890): IsRelativeURL() should accept string_views.
-  if (!IsRelativeURL(base_spec.data(), base_parsed, relative, relative_length,
+  if (!IsRelativeURL(base_spec.data(), base_parsed, relative.data(),
+                     relative.length(),
                      (base_is_hierarchical || is_hierarchical_base),
                      &is_relative, &relative_component)) {
     // Error resolving.
@@ -389,16 +379,15 @@
     Parsed base_parsed_authority = ParseStandardURL(base_spec);
     if (base_parsed_authority.host.is_nonempty()) {
       STACK_UNINITIALIZED RawCanonOutputT<char> temporary_output;
-      bool did_resolve_succeed =
-          ResolveRelativeURL(base_spec.data(), base_parsed_authority, false,
-                             relative, relative_component, charset_converter,
-                             &temporary_output, output_parsed);
+      bool did_resolve_succeed = ResolveRelativeURL(
+          base_spec.data(), base_parsed_authority, false, relative.data(),
+          relative_component, charset_converter, &temporary_output,
+          output_parsed);
       // The output_parsed is incorrect at this point (because it was built
       // based on base_parsed_authority instead of base_parsed) and needs to be
       // re-created.
-      DoCanonicalize(temporary_output.data(), temporary_output.length(), true,
-                     REMOVE_WHITESPACE, charset_converter, output,
-                     output_parsed);
+      DoCanonicalize(temporary_output.view(), true, REMOVE_WHITESPACE,
+                     charset_converter, output, output_parsed);
       return did_resolve_succeed;
     }
   } else if (is_relative) {
@@ -412,15 +401,13 @@
     // TODO(crbug.com/350788890): ResolveRelativeURL() should accept
     // string_views.
     return ResolveRelativeURL(base_spec.data(), base_parsed, file_base_scheme,
-                              relative, relative_component, charset_converter,
-                              output, output_parsed);
+                              relative.data(), relative_component,
+                              charset_converter, output, output_parsed);
   }
 
   // Not relative, canonicalize the input.
-  // TODO(crbug.com/350788890): DoCanonicalize() should accept string_views.
-  return DoCanonicalize(relative, relative_length, true,
-                        DO_NOT_REMOVE_WHITESPACE, charset_converter, output,
-                        output_parsed);
+  return DoCanonicalize(relative, true, DO_NOT_REMOVE_WHITESPACE,
+                        charset_converter, output, output_parsed);
 }
 
 template <typename CHAR>
@@ -465,8 +452,8 @@
     // may have changed with the different scheme.
     STACK_UNINITIALIZED RawCanonOutput<128> recanonicalized;
     Parsed recanonicalized_parsed;
-    DoCanonicalize(scheme_replaced.data(), scheme_replaced.length(), true,
-                   REMOVE_WHITESPACE, charset_converter, &recanonicalized,
+    DoCanonicalize(scheme_replaced.view(), true, REMOVE_WHITESPACE,
+                   charset_converter, &recanonicalized,
                    &recanonicalized_parsed);
 
     // Recurse using the version with the scheme already replaced. This will now
@@ -822,23 +809,21 @@
   return host_info.IsIPAddress();
 }
 
-bool Canonicalize(const char* spec,
-                  int spec_len,
+bool Canonicalize(std::string_view spec,
                   bool trim_path_end,
                   CharsetConverter* charset_converter,
                   CanonOutput* output,
                   Parsed* output_parsed) {
-  return DoCanonicalize(spec, spec_len, trim_path_end, REMOVE_WHITESPACE,
+  return DoCanonicalize(spec, trim_path_end, REMOVE_WHITESPACE,
                         charset_converter, output, output_parsed);
 }
 
-bool Canonicalize(const char16_t* spec,
-                  int spec_len,
+bool Canonicalize(std::u16string_view spec,
                   bool trim_path_end,
                   CharsetConverter* charset_converter,
                   CanonOutput* output,
                   Parsed* output_parsed) {
-  return DoCanonicalize(spec, spec_len, trim_path_end, REMOVE_WHITESPACE,
+  return DoCanonicalize(spec, trim_path_end, REMOVE_WHITESPACE,
                         charset_converter, output, output_parsed);
 }
 
diff --git a/url/url_util.h b/url/url_util.h
index 8ca0eec..3689cf4f 100644
--- a/url/url_util.h
+++ b/url/url_util.h
@@ -220,15 +220,13 @@
 // output and parsed structures will still be filled and will be consistent,
 // but they will not represent a loadable URL.
 COMPONENT_EXPORT(URL)
-bool Canonicalize(const char* spec,
-                  int spec_len,
+bool Canonicalize(std::string_view spec,
                   bool trim_path_end,
                   CharsetConverter* charset_converter,
                   CanonOutput* output,
                   Parsed* output_parsed);
 COMPONENT_EXPORT(URL)
-bool Canonicalize(const char16_t* spec,
-                  int spec_len,
+bool Canonicalize(std::u16string_view spec,
                   bool trim_path_end,
                   CharsetConverter* charset_converter,
                   CanonOutput* output,
diff --git a/url/url_util_unittest.cc b/url/url_util_unittest.cc
index dde575d..2e42d3f 100644
--- a/url/url_util_unittest.cc
+++ b/url/url_util_unittest.cc
@@ -51,7 +51,7 @@
     StdStringCanonOutput output(&canonicalized);
     Parsed parsed;
     bool success =
-        Canonicalize(url_case.input.data(), url_case.input.size(),
+        Canonicalize(url_case.input,
                      /*trim_path_end=*/false,
                      /*charset_converter=*/nullptr, &output, &parsed);
     output.Complete();
@@ -233,8 +233,7 @@
   // Make sure the input is canonicalized.
   RawCanonOutput<32> original;
   Parsed original_parsed;
-  Canonicalize(base_url, strlen(base_url), true, nullptr, &original,
-               &original_parsed);
+  Canonicalize(base_url, true, nullptr, &original, &original_parsed);
 
   Replacements<char> replacements;
   replacements.SetScheme(scheme, Component(0, strlen(scheme)));
@@ -449,7 +448,7 @@
   Parsed original_parsed;
   RawCanonOutput<32> original;
   const char* url = "htt\nps://example.com/<path";
-  Canonicalize(url, strlen(url), false, nullptr, &original, &original_parsed);
+  Canonicalize(url, false, nullptr, &original, &original_parsed);
   ASSERT_TRUE(original_parsed.potentially_dangling_markup);
 
   // Perform a replacement, and validate that the potentially_dangling_markup
@@ -468,7 +467,7 @@
   Parsed original_parsed;
   RawCanonOutput<32> original;
   const char* url = "http://example.com/\n/<path";
-  Canonicalize(url, strlen(url), false, nullptr, &original, &original_parsed);
+  Canonicalize(url, false, nullptr, &original, &original_parsed);
   ASSERT_TRUE(original_parsed.potentially_dangling_markup);
 
   // Perform a replacement, and validate that the potentially_dangling_markup
@@ -532,7 +531,7 @@
   std::string canonicalized;
   StdStringCanonOutput output(&canonicalized);
   Parsed parsed;
-  if (!Canonicalize(spec.data(), spec.size(), trim_path_end,
+  if (!Canonicalize(spec, trim_path_end,
                     /*charset_converter=*/nullptr, &output, &parsed)) {
     return {};
   }
diff --git a/v8 b/v8
index b2f16db..c01e7ca 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit b2f16db913db5f9ac7b2c7096c6f6c13326d1377
+Subproject commit c01e7ca8a87d9a0d1d51e9a0dcf8c1eb44b6e3d4